Transforming HTML With Netlify Edge Functions

I’ve long wanted the ability to create custom collections of icons from my icon gallery.

Today I can browse collections of icons that share pre-defined metadata (e.g. “Show me all icons tagged as blue”) but I can’t create your own arbitrary collections of icons.

That is, until now!

I created a page at /lookup that allows you to specify however many id search params you want and it will pull all the matching icons into a single page.

Here’s an example of macOS icons that follow the squircle shape but break out of it ever-so-slightly (something we’ll lose with macOS Tahoe).

It requires a little know how to construct the URL, something I’ll address later, but it works for my own personal purposes at the moment.

So how did I build it?

Implementation

So the sites are built with a static site generator, but this feature requires an ability to dynamically construct a page based on the icons specified in the URL, e.g.

/lookup?id=foo&id=bar&id=baz

How do I get that to work? I can’t statically pre-generate every possible combination[1] so what are my options?

  1. Create a “shell” page that uses JavaScript to read the search params, query a JSON API, and render whichever icons are specified in the URL.
  2. Send an HTML page with all icons over the wire, then use JavaScript to reach into the DOM and remove all icons whose IDs aren’t specified in the page URL.
  3. Render the page on the server with just the icons specified in the request URL.

No. 1: this is fine, but I don’t have a JSON API for clients to query and I don’t want to create one. Plus I have to duplicate template logic, etc. I’m already rendering lists of icons in my static site generator, so can’t I just do that? Which leads me to:

No. 2: this works, but I do have 2000+ icons so the resulting HTML page (I tried it) is almost 2MB if I render everything (whereas that same request for ~4 icons but filtered by the server would be like 11kb). There’s gotta be a way to make that smaller, which leads me to:

No. 3: this is great, but it does require I have a “server” to construct pages at request time.

Enter Netlify’s Edge Functions which allow you to easily transform an existing HTML page before it gets to the client.

To get this working in my case, I:

  1. Create /lookup/index.html that has all 2000+ icons on it (trivial with my current static site generator).
  2. Create a lookup.ts edge function that intercepts the request to /lookup/index.html
  3. Read the search params for the request and get all specified icon IDs, e.g. /lookup?id=a&id=b&id=c turns into ['a','b','c']
  4. Following Netlify’s example of transforming an HTML response, use HTMLRewriter to parse my HTML with all 2000+ icons in it then remove all icons that aren’t in my list of IDs, e.g. <a id='a'>…</a><a id='z'>…</a> might get pruned down to <a id='a'>…</a>
  5. Transform the parsed HTML back into a Response and return it to the client from the function.

It took me a second to get all the Netlify-specific configurations right (put the function in ./netlify/edge-functions not ./netlify/functions, duh) but once I strictly followed all of Netlify’s rules it was working! (You gotta use their CLI tool to get things working on localhost and test it yourself.)

Con-clusions

I don’t particularly love that this ties me to a bespoke feature of Netlify’s platform — even though it works really well!

But that said, if I ever switched hosts this wouldn’t be too difficult to change. If my new host provided control over the server, nothing changes about the URL for this page (/lookup?id=…). And if I had to move it all to the client, I could do that too.

In that sense, I’m tying myself to Netlify from a developer point of view but not from an end-user point of view (everything still works at the URL-level) and I’m good with that trade-off.