Add personalization to static HTML with Netlify Edge Functions — no browser JavaScript required
Tags: netlify, html, serverless, javascript
Published: 2022-08-12 19:05:54

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](https://astro.build/) and [Eleventy](https://www.11ty.dev/) 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](https://docs.netlify.com/netlify-labs/experimental-features/edge-functions/api/?utm_campaign=devex-san&utm_source=whitep4nth3r-blog&utm_content=ef-context-object#netlify-specific-context-object) 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.

{% youtube https://www.youtube.com/watch?v=6pEVhH37xQE %}

## 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](https://www.netlify.com/img/deploy/button.svg)](https://ntl.fyi/3C1Uwi2)

## 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.

- [Node.js](https://nodejs.org/en/) (LTS recommended)
- [GitHub CLI](https://cli.github.com/)
- [Netlify CLI](https://docs.netlify.com/cli/get-started/?utm_campaign=devex-san&utm_source=netlify-blog&utm_term=docs-cli-get-started&utm_content=docs-cli-get-started)

### 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](https://github.com/whitep4nth3r/getting-started-with-netlify-edge-functions/blob/main/public/index.html).

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`.

![A screenshot of VSCode, showing two html files in a public directory. The visible code is the hello template file with the location data placeholder.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hhdp0rh3lzl5la7tjaad.png)

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](https://docs.netlify.com/configure-builds/file-based-configuration/?utm_campaign=devex-san&utm_source=whitep4nth3r-blog&utm_content=docs-file-config). 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.

```toml
[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:

```bash
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](http://localhost:8888), and hello-template.html on [http://localhost:8888/hello-template](http://localhost:8888/hello-template). You’re now ready to transform this static HTML file with an Edge Function!

![A browser screenshot, showing the hello template file with the placeholder location unknown](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ekl0ko059bcdrtf0agp.png)

### 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`.

![A screenshot of VSCode, showing the transform hello template js file added into an edge functions directory, inside a netlify directory.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdbzp8xhdb3hwbrwkbj5.png)
 
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.

```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.

```javascript
// 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
  // 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, 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](https://developer.mozilla.org/en-US/docs/Web/API/Request), and `context` — which is a [Netlify specific API](https://docs.netlify.com/netlify-labs/experimental-features/edge-functions/api/?utm_campaign=devex-san&utm_source=whitep4nth3r-blog&utm_content=ef-context-object#netlify-specific-context-object) 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](https://developer.mozilla.org/en-US/docs/Web/API/Response) containing the updated page. Navigate to the following URL in your browser: [http://localhost:8888/hello-template?method=transform](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! 🏆

![A screenshot of the finished result in the browser, where the placeholder template has been updated with Manchester, United Kingdom](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rfhre58poeonfwmw80g.png)

### 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](https://docs.netlify.com/site-deploys/overview/?utm_campaign=devex-san&utm_source=whitep4nth3r-blog&utm_term=site-deploys-overview&utm_content=site-deploys-overview). 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:

```bash
git init
```

Stage and commit your files:

```bash
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:

```bash
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).

```bash
netlify login
```

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

```bash
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.

```bash
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?](https://youtu.be/qWlOfC-HwiQ?t=1024)” 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.