Guides & Tutorials

Add personalization to static HTML with Netlify Edge Functions — no browser JavaScript required

Personalization — the process of creating customized experiences for visitors to a website — is a hot topic on the web in 2022. In a crowded world of digital experiences clambering for our attention, businesses are turning to personalization to connect more deeply with their customers, and ultimately, generate more sales and growth.

And shipping less client-side JavaScript is an even hotter topic. New and emerging front-end frameworks such as Astro and Eleventy are pushing the boundaries of the JavaScript framework era of the late 2010s, making it their core mission to ship less and less JavaScript by default, and bring developers’ focus back to a lean, native and performant web.

The great news is that with Netlify Edge Functions you can achieve dynamic personalization with no client-side JavaScript — resulting in a great developer experience, and an even better end-user outcome. There’s also no need introduce the overhead of a front-end framework simply to gain this ability in your projects.

In this video tutorial, I show you how to use Netlify Edge Functions to intercept an HTTP request on a static HTML page, transform the response on the fly by injecting the geolocation data of the request, and return the updated response to the browser.

Deploy the tutorial code to Netlify

If you want to dive in immediately, you can fork the tutorial’s code repository on GitHub, and deploy the site to your own Netlify account in seconds. Click on the Deploy to Netlify button below, and take it for a spin!

Deploy to Netlify Button

Build it from scratch

Alternatively, if you’d like to build out the code from scratch, you can follow the guide below to create a simplified version of the project, without the hello world Edge Function included in the video tutorial.

Prerequisites

Before getting started, check you’ve got the tools you’ll need to complete the tutorial.

Setup a project to serve static HTML files

In a new project directory, create a public directory. And inside that, add an index.html, and a file named hello-template.html.

Add some basic boilerplate to the index.html file (if you’re using VSCode you can type ! followed by tab in an HTML file to generate some HTML boilerplate), or you can copy and paste the file from the tutorial repository.

Next, add some similar boilerplate to hello-template.html, as well as some placeholder content that we’ll replace using an Edge Function in the next step. We’re going to be replacing the placeholder with some geolocation data, so I’ve named my placeholder LOCATION_UNKNOWN.

Screenshot of VSCode, showing a public directory with index.html and hello-template.html inside. Hello-template.html is in view, and shows HTML boilerplate and the location placeholder.

Next, we’re going to set up the project to serve those two static HTML files from the public directory using Netlify’s file-based configuration. At the root of your project, add a netlify.toml file. This is a configuration file that specifies how Netlify builds and deploys your site — including redirects, branch settings, edge function routing and more.

Add the following code to netlify.toml to instruct Netlify to serve your static files from the public directory.

[build]
  publish = "/public"

Let’s check everything’s wired up. In a terminal window at the root of your project, run the following command to start up a development server with the Netlify CLI:

netlify dev

When the development server is up and running, head on over to your browser, and you’ll see your index.html on http://localhost:8888, and hello-template.html on http://localhost:8888/hello-template. You’re now ready to transform this static HTML file with an Edge Function!

hello template in the browser, which reads Hello from an HTML template. This is a static HTML file stored in the public directory. Your location LOCATION_UNKNOWN

Transform an HTTP response with an Edge Function

We’re going to write an Edge Function to intercept a request to hello-template using a URL query parameter (?method=transform), grab the geolocation data of the request, and transform the HTTP response to include the city and country name.

You can write Edge Functions using JavaScript or TypeScript. In this tutorial, we’re going to write Edge Function code in JavaScript. Edge Function files live in a special directory so that Netlify knows how to bundle them when you deploy your site. At the root of your project, create a netlify directory, and inside that add an edge-functions directory. Create a new file inside the edge-functions directory and name it transform-hello-template.js.

Screenshot of VSCode, showing the new edge function file in the edge functions directory

Before we write the Edge Function code, let’s wire up the file to the development environment. In your netlify.toml file, add the following code, below the build settings. This tells Netlify to run the transform-hello-template function when a user requests the hello-template page in the browser. Stop and start your development server so the CLI picks up the changes in netlify.toml.

# netlify.toml

[[edge_functions]]
  path = "/hello-template"
  function = "transform-hello-template"

In transform-hello-template.js, add the following code. And let’s unpack what it does.

// netlify/edge-functions/transform-hello-template.js

export default async (request, context) => {
  const url = new URL(request.url);

  // Look for the "?method=transform" query parameter, and return if we don't find it
  if (url.searchParams.get("method") !== "transform") {
    return;
  }

  // Get the page content that will be served next
  // In this tutorial example, it will be the content from hello-template
  const response = await context.next();
  const page = await response.text();

  // Search for the placeholder
  const regex = /LOCATION_UNKNOWN/i;

  // Get the location from the Context object
  const location = `${context.geo.city}, ${context.geo.country.name}`;

  // Replace the content with the current location
  const updatedPage = page.replace(regex, location);

  // Return the response
  return new Response(updatedPage, response);
};

We start by exporting a default async function. Edge Functions take two parameters, request — which represents the incoming HTTP request, and context — which is a Netlify specific API that exposes geolocation data, lets you work with cookies, rewrites, site information and more.

First, we parse the URL from the request. Then, we look for the method=transform query parameter on the URL, using url.searchParams.get(). If we don’t find the required query parameter on the URL, we return and exit out of the Edge Function early, which will serve the hello-template with the placeholder unchanged.

If we do find ?method=transform on the URL, we use context.next() to grab the next request in the HTTP chain — in this case it’s our hello-template file — and then we grab the text content of the page, assigning it to the variable page. Next, we use a regular expression to search for the placeholder in the text of hello-template, which we want to replace with geolocation data.

Geolocation data is available on the context object, as context.geo. Using JavaScript string interpolation, we can construct a friendly, readable string comprising the city and country name, which we then use to replace the string matched by the regular expression.

Finally, we return a new HTTP Response containing the updated page. Navigate to the following URL in your browser: http://localhost:8888/hello-template?method=transform — and you’ll see the placeholder updated with Edge-powered geolocation data! Personalization with no JavaScript: achievement unlocked! 🏆

Screenshot of hello template, showing the location placeholder as been replaced by Manchester, United Kingdom

Add git version control

Now that we have the project working locally, we have a number of options for how we deploy it to Netlify. In this tutorial, we’ll connect to a new git repo so that we get CI/CD all rolled in and configured for free.

Initialize a new git repository by running the following command:

git init

Stage and commit your files:

git add . # stage your files
git commit -m 'Initial commit' # commit your files with a commit message

The next steps will take you through adding your repository to GitHub via the GitHub CLI — but you can push your project to GitHub however you’re most comfortable.

Run the following command in your terminal to create a new repository that’s connected to your GitHub account:

gh repo create

When prompted on the kind of repository you’d like to create, select: Push an existing local repository to GitHub. Follow the remaining prompts to fill out the relevant project details. Now, you’re ready to deploy to Netlify!

Deploy to Netlify using the Netlify CLI

If you’re not already logged in to the Netlify CLI, run the following command in your terminal, and follow the prompts to authorize with Netlify (a browser window will open).

netlify login

Next, run the following command to initialize a new project on Netlify.

netlify init

Fill out the following prompts to set up the new project:

  • What would you like to do? Create & configure a new site
  • Team: YOUR_TEAM
  • Site name (optional): CHOOSE_UNIQUE_SITE_NAME

The following prompts will be pre-filled given that we’ve provided a netlify.toml file in the repository. Hit enter to use the defaults.

  • Your build command (hugo build/yarn run build/etc):  (we're not using a framework or a build script, so this stays blank!)
  • Directory to deploy (blank for current dir): (public)
  • Netlify functions folder: (netlify/functions)

Wait a few seconds… and your new site is deployed! 🎉

Run the following commands in your terminal to open the site in your browser, and open the Netlify admin panel.

netlify open:site # open the site in your browser
netlify open:admin # open the Netlify admin panel in your browser

And you’re done! Now you can test out your no-JavaScript-personalization-at-the-edge by sending the site URL to your friends, and showing off your new, shiny skills!

Final thoughts

When we talk about the new-found power that Edge Functions bring us as developers and users of the web, the question that often comes to mind is, “Should I put everything on The Edge, now?” Whilst the answer is usually, “It depends,” it’s always worth framing these new tools and technologies as additions to the evolving web ecosystem rather than replacements of existing capabilities.

Whilst The Edge certainly gives us a different set of advantages over serverless functions — most notably being able to run code from the closest server location possible to any user around the world — there are times when you might need control over where your code runs in the cloud, perhaps at the closest location to your database services, for example. In any case, Edge Functions are a brilliant use case for reducing client-side JavaScript in the browser, whilst still being able to provide a personalized experience for users of the web.

And I, for one, am excited about what else is to come.

Keep reading

Recent posts

How do the best dev and marketing teams work together?