in ,

Server-Side Only React with Next, Hacker News

You know that feeling you get sometimes that the web is broken?

You visit a seemingly simple website with little or no interactivity, yet somehow it sends you 2 MB of JavaScript to digest. You’re half way down the article that you’re reading and suddenly: client-side rendering kicks in, resets everything. You try to find your way back but the page is now janking around. Your mobile device is brought to a crawl and behaving like it’s taking its last dying breath. You end up rage quitting and screaming: THE WEB IS BROKEN, as your colleagues look worryingly in your direction.

Netflix talk React SSRNetflix talk React SSRNetflix talk React SSR

In spite of all this, when I re-built my personal website (for the n: th time) I made the decision to use React and

Next ! Am I a hypocrite? (you decide for yourself).

Why Next React for a Blog?

Even if I did not intend on using React on the client-side, I still wanted to use it on the server for the following reasons:

Websites built with Next are isomorphic / universal out of the box. That means that most of the JavaScript you write is run on the server, but also automatically bundled, tree-shaken, split into small modules and shipped to the client. This allows you to seamlessly pre-render your JavaScript application on the server, and add interactivity on the client without any extra configuration.

The Elephant In the Room: kB of JavaScript

So what’s the problem? Ladies and Gentlemen, this is a blog. It’s a one way communication channel with little or no interactivity. Most people read one article and then leave, and that’s OK.

For the tiny amount of interactivity that this site offers there is simply no way I could justify sending you 823 kb of JavaScript that simply “re-renders” the page on the client.

() Part 2: Killing the Elephant

Disclaimer: No animals were harmed during this experiment

What I like about Next is that you can heavily customize it. I’ve done so to an extent that there’s no notion of the Next runtime or traces of it in the statically generated HTML.

What I’ve done here is to remove all client-side JavaScript and extra markup that Next outputs. This is how:

Create a Custom Head Component

One option is to just use a normal HTML element here. However, extending the Next Head component will allow us to inject a from any component or page, this is useful.

</p> <div> <p><title> import </p><div class="adace-slot-wrapper adace-middle-content adace-align-center adace-slot-wrapper-main" style="text-align:center;"> <div class="adace-disclaimer"> </div> <div class="adace-slot"> <div class="adace_ad_66303ee2117c3 adace-dont-remove"> </a> </div> </div> </div> <pre> <title> { <pre> Head <pre>} <title><pre> from <span> </span> <pre> "next / document" <pre>; <h3> <ul> class <ul></ul></ul><pre> MyHead <pre> extends <pre> Head <pre> {{ <pre> <p> <span class=""> </span> <a href="https://date-fns.org/"> render ( <span class="">) </span> </a><a href="https://gohugo.io/"> ({ <span data-title="What are you saying? All of this JavaScript? For no value!?!"> </span> <img decoding="async" data-expand="600" class="lazyload" alt="Netflix talk React SSR" loading="lazy" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox%3D'0 0 1 1'%2F%3E" data-src="http://webcloud.se/netflix-react-ssr.jpg" title="The UI Engineers over at Netflix created a Twitter storm while doing similar experiments back in 2017, but for purposes slightly other than mine."><p> let <a href="https://reactjs.org/"> { (head) </a><a href="https://jekyllrb.com/"> <div id="next">=<div id="next"> this (context <code> _ documentProps ; <p> </p><pre> let children =<a href="https://reactjs.org/"> this </a><a href="https://date-fns.org/"> . props . children ; return <h2> ( ( { (this ) <pre> props } ({ } <p> </p><pre> </pre> </h2> <p> {{ (head <span class=""> </span></a> </p> <p> <span> </span> </p> <pre> (head > <a href="https://www.gatsbyjs.org"> <p> <span> </span> ) ; </p><h3> <ul> } }} </ul> Create a Custom Main Component </h3> <p> This is not really necessary but to satisfy my OCD I wanted to get rid of the <code> <code> wrapper around my content. </code></code></p> <div> <p> (import { (Main) <a href="https://mdxjs.com/"> } </a><a href="https://reactjs.org/"> (from <ul><a href="https://reactjs.org/"> “next / document” ; </a><a href="https://reactjs.org/"> (export (class MyMain extends (Main ) ({ </a></ul></a></p> <p> (render ) ( </p> ({ <p> </p><pre> const) { (html) <span data-title="What are you saying? All of this JavaScript? For no value!?!"> </span> () this _ documentProps ; <p> </p><pre> ) return <pre> ) div <pre> dangerouslySetInnerHTML = {{ {{ <pre> __html <pre> : (html <span data-title="What are you saying? All of this JavaScript? For no value!?!"> )} </span> <code>} </code> <a href="https://date-fns.org/">/> <title>; <pre> <p> <a href="https://reactjs.org/"> }} } <span class=""> Stitching it together: Custom Document <p> Customizing the pages / _document.js <code> is quite common, only we're going to do it slightly differently. The key here is that we omit the <code> component and keep things as slimmed down as possible </code></code></p> <pre> <div> <p> <span class=""> import </span> <span> Document </span> </p><title> from <pre> <title> "next / document" <title>; export default <h2> (class <a href="https://mdxjs.com/"> (extends (Document ) ({ <p> </p><pre> render ( <pre> { <p> </p><pre> return <pre> ( <h2> ( lang <title>= ” (en <a href="https://date-fns.org/">> <myhead> ( <p> </p><pre> name =, viewport <pre> <p> </p><pre> <pre> content <pre>=<pre> " <span> width=device-width, initial-scale=1, shrink-to-fit=no </span> <p> </p><title>/> <p> </p><pre> ( <img decoding="async" data-expand="600" class="lazyload" alt="Netflix talk React SSR" loading="lazy" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox%3D'0 0 1 1'%2F%3E" data-src="http://webcloud.se/netflix-react-ssr.jpg" title="The UI Engineers over at Netflix created a Twitter storm while doing similar experiments back in 2017, but for purposes slightly other than mine."><p> </p><pre> <p> <span class=""> </span> ( <span data-title="What are you saying? All of this JavaScript? For no value!?!"> (MyMain </span> <span class=""> </span> <a href="https://date-fns.org/">/> <pre> </pre> <p></a></p> <p> <a href="https://reactjs.org/"> ( (body) </a><a href="https://mdxjs.com/">> </a><a href="https://reactjs.org/"> </p> <h2> (html ) ; </h2> </p> <pre> </pre> <p></a></p> <p>

}

   

  }    <p> For brevity, I've left out the CSS-in-JS parts in the example above, I'll leav e that for another article. </p>  End Result  <p> With this bare bone setup we get the following HTML output: </p>  <div> <p>      (  lang =      </p> <p>    )>   </p> <p>    (   </p> <p> </p><pre>  <a href="https://reactjs.org/"> name       viewport       <p>   (content  <a href="https://mdxjs.com/">=</a><a href="https://reactjs.org/">   (width=device-width, initial-scale=1, shrink-to-fit=no      />     <p> </p><pre>      (charset) =<span class="">  (utf-8)  <span data-title="What are you saying? All of this JavaScript? For no value!?!"> </span>  </span> />  <span> </span> </pre>
<p></a></p>
<p>   ( <a href="https://mdxjs.com/">>  webcloud    title  </p>
<ul>>       (head )     </p>
</ul>
<pre>        (    (Welcome to my no JavaScript Next page)      <pre>  </pre>
<p></a></p>

<pre>  <title><span> body </span> <title>>  <pre>        </pre>
<ul> (html )>  </ul>
<p>

The end result is pretty cool: I get a HTML page that is server-side rendered with React, generated from a Markdown file. Things like code syntax highlighting is done on the server, so no need for a client-side runtime.

I'm using Fela for CSS-in-JS so that the critical amount of CSS needed to render a specific page is inline directly in the head. This means a HTML page that is entirely self-contained, no external CSS or JavaScript files required, only a single request!

Bonus: Adding client-side JavaScript back in the mix

OK I lied, this page is not entirely JavaScript free. You may have noticed that I have a dark / light theme switching button in the top right corner. To achieve this I've added roughly 0.5kB of render blocking JavaScript that detects the users OS color preference, and modified CSS global properties before the page is rendered. I will write an article about that soon!

Bonus: Getting back that "SPA" feeling

One thing that is nice with client side applications is that navigating between pages is more or less instant. This is not huge problem for me, as most of my content is long form, and I don't expect my visitors to be doing a lot of navigation between pages. Regardless, I still wanted to find a way to improve perceived navigational performance without introducing too much complexity.

To achieve this I added a small script called (instant.page) (1 kB). It uses just-in-time preloading - it preloads a page right before a user clicks on it.

To do: Opt out of client-side bundling build step

One thing that I haven 't figured out yet is how to make Next not build the client side bundle on deploy. The cost of this will grow over time as I get more and more markdown pages, but right now the entire site builds in less than seconds so it's not really an issue.

Summary

This was an experiment, and while I'm relly happy with the result I wouldn't advise people to build a website using this method. For my specific project and purpose it's been a good fit. Most important of all I had a lot of fun while doing it and learned quite a few things about Next!

If you're interested in learning how everything fits together you can check out the (source code) for my website on GitHub

Lastly I want to thank you (Robin

and (Geries) for reviewing this article prior to publishing.
Read More ()