ssr for hackers

if it don't be, i can make it do.

what's ssr?

So back in the day, javascript didn't have a bunch of cool frameworks. most people used jquery, and avoided JS as much as humanly possible.

When someone made a request to a website:

  1. The server saw a path like /posts/hello
  2. It fetched all the content from a database or something
  3. It rendered the HTML
  4. It sent it back to the user

So before any javascript even ran, you'd get something like this back:

And that was it! Rendering the content on the server is called "server side rendering". But holy boy times have changed and things got a whole lot more bizarre.

client-side rendering?

Most javascript frameworks and modern technologies do "client side rendering", meaning they do the following sequence of steps:

  1. The server saw a path like /posts/hello
  2. It fetched all the content from a database or something
  3. It sent that raw data to the user
  4. JavaScript rendered the HTML

And now, before any scripts ran on the page, your browser gets something like this:

Here, FancyBoi could be React, Vue, or Elm. They all wait until the javascript has loaded to render the view the user sees!

how do we get ssr?

So rendering Elm in HTML looks something like this:

When the JavaScript on the page loads, Elm renders this under the hood:

  • Functions like div, h1, and h2 take in a list of attributes followed by a list of children.
  • Functions like text just take a String and render it to the DOM.

If we wanted to write Elm code that rendered on the server, we'd need a way to render the HTML data structure as a string on the backend. The elm/html package wasn't designed for folks to do things like this, so we'll need to roll our own shady garbage.

For this experiment, we can create our own custom type to capture the types of HTML elements we want to render!

Here we say Html is one of two things:

  1. A node with attributes and children
  2. Some text we want to render

making Ssr.Html

In a file at src/Ssr/Html.elm let's build an API on top of that data structure:

We'll also need to define src/Ssr/Attributes.elm to support adding attributes and events:

aaaand... it's useless.

With this API, we're able to write HTML that looks just like elm/html, the only difference is we need to update our import statement to use the new module:

The new Ssr.Html and Ssr.Attributes are working great for building up data structures, but we can't do anything with them! We need to actually render Html:

  1. The static server-side markup like <h1>Hello</h1>
  2. The living, breathing HTML that Elm uses so we can click buttons and do actual stuff!

For that reason, Ssr.Html and Ssr.Attributes need to expose two new functions:

Now we can have two entrpoints to our app, src/Main/Ssr.elm and src/Main/Client.elm that reuse that same Ssr.Html markup for different outputs:

When it comes time to call elm make, src/Main/Ssr.elm is used by NodeJS to generate static HTML files, while src/Main/Client.elm is used to rehydrate the app when the JavaScript loads.

That results in something like this:

A slick 98/100/100/100 google lighthouse raiting, because this page is blazing fast and accessible bro

want more detail?

You can check out the source code on GitHub:

Thanks for reading!

next up:

code generation with elm

February 10th, 2020