Introducing Server Components

The developer experience of web components, with the power and simplicity of server-side development.

We’ll start with a little background. Feel free skip to “Let’s Fix This” below, or straight to the docs if you’d rather get to the meat ASAP.

Web development has gone through a revolution in recent years, with much of the hard work jumping to the client-side. Developers have moved from writing swathes of HTML themselves to building JavaScript codebases that abstract over the top, building single-page web applications.

The frameworks that underpin this give us powerful & delightful ways to quickly create web pages, but along the way they’ve had to reinvent basic parts of browser functionality, drastically complicate our build processes and break (and add more complexity to sort-of fix) accessibility & SEO. The enormous hunks of JavaScript we’ve written to solve these issues is now sent to every single one of our users, on every page load, and CPU cycles everywhere are spent rerunning all of this for everybody individually.

JavaScript client-side frameworks are fighting against the way the web was designed. You can see this most visibly in the reimplementation of web builtins, from history tracking to accessibility hooks to page rendering; all problems previously solved by browsers, now resurrected to be solved separately in each page’s own scripts. These frameworks are fantastic, and they’re making great headway despite this, but going against the stream has downsides, creating extra complexity and pain, and wasting the web’s virtues.

Frameworks come with a heavy cost

Paul Lewis from the Chrome team recently took a look at The Cost Of Frameworks, especially on mobile, analysing time-to-interactive for various TodoMVC implementations (looking only at client-side processing cost, not JS transfer time or paint performance):

For me the results are pretty clear: there appears to be a pretty hefty tax to using Frameworks on mobile.

Tom Dale (one of the creators of Ember.js), fired back:

Critics of frameworks tend to totally miss the value that they provide for people. … Frameworks let you manage the complexity of your application as it and the team building it grows over time. All of the other stuff is just gravy.

This is key, and certainly true: the JS frameworks we use today are fantastic tools to effectively and enjoyably build complex web applications. At the same time, Paul’s right, they’re creating pain for our users. Damn.

Paul Irish (from the Chrome DevTools team) did some performance analysis too, looking at how JS frameworks affected performance in Reddit Mobile (built on React). Highlights:

There’s way too much javascript here. While the development stack (babel, react, npm, etc) is modern and feels great, it ends up shoving a lot of JS at the browser to make it all work.

22 seconds (!!!) spent rendering page components on mobile.

The core UX concern here is that the page is unresponsive to user input for up to 17 seconds while it’s loading. As interacting with reddit means bouncing to links and coming back, the “loading” process will actually happen quite often.

As that last quote touches on; Reddit Mobile is not an application that really gets much from front-end JavaScript. Using Reddit means flitting between a series of pages, many of which necessitate full reloads anyway. Polish and interactivity from JS is useful, but the core experience is a traditional one.

We were building sites like this 10 years ago just fine, managing their complexity as it grew along the way, and they were much faster than this.

All these are interesting analyses, and they’re not alone. Read Karolina Szczur on how “The Web Isn’t Uniform”, Zack Argyle from Pinterest on why you should “Stop Using React For Everything” and Jake Archibald from Google on how “Progressive Enhancement is Faster”. Google’s own new Accelerated Mobile Pages standard — now required if you want to be a top listed news story — totally forbids arbitrary JavaScript. We’re seeing a wide range of push back against the recent domination of client-side JS frameworks, especially with the explosion of mobile browsing.

At the same time, Tom’s rebuttal above is not wrong.

JavaScript frameworks give us magical developer ergonomics, providing fantastic tools to manage the complexity of modern web development. That has huge value. While in many cases these JS frameworks haven’t helped page performance, they’ve drastically improved developer experience.

It’s surprising that moving code to solve the same problem from a server into our users’ browsers is what hugely improves life for developers though. And indeed, really it’s not that change that’s making our lives easier. Moving code to the client side has allowed rapid innovation in how we build pages, and shown the way for frameworks to build with new designs and concepts. It’s these new approaches though that are changing developer ergonomics, not where their specific implementation is being run.

Modern JS frameworks are more powerful and expressive ways to build the web primarily because of the abstractions and ideas they build with, not because they’re running in browsers instead of servers.

We can have both. We can create and use powerful abstractions and manage complexity when building our web pages, and do it on the server-side alone. You can have a fantastic developer experience without React/Ember/Angular/Backbone/Knockout/Ampersand/Mithril/whatever.

For the 90% of sites, server-side behaviour is enough. You can build all the core experience server-side, drop all this pain and complexity, and still get the benefits. You can have your developer ergonomics cake, and eat it too.

We’ll talk about the last 10% later.

Let’s Fix This

Server Components is a simple tool to help you build web pages better.

It lets you define and compose together chunks of standalone logic, view and behaviour, to easily build powerful and intricate user interfaces, while staying entirely on the server-side. It’s not a framework: it doesn’t dictate the structure of your code, it doesn’t add any build steps, or add any extra weight on the client-side at all (literally zero). Search engines will love you, performance and accessibility become (nearly) free, your crush will realise that that hairstyle does look great, and you’ll finally find time to learn Spanish (and catch up on House of Cards).

But how? What witchcraft is this?

Server Components are web components for Node.js

Web components, for those not familiar, are a series of new specs coming (slowly) to browsers everywhere. It’s actually a set of 4 new features (Custom Elements, HTML Templates, Shadow DOM and HTML Imports), which together provide a fantastic, fast, built-into-the-browser way of defining new elements, with their own internal rendering logic and behaviour, and composing them together into a page. Building your web page becomes a process of defining or just downloading small composable blocks, and then declaratively clicking them together into a full page.

This declarative clicking together of vertical slices of application is the key ergonomic improvement from JS frameworks. Each framework disagrees on everything else developers really want (Ember’s convention-heavy approach, Knockout’s complete flexibility, React’s functional purity, Ampersand’s modularity), but over the last couple of years every single one of them has moved onto a model where you compose together standalone components. Web components provide a standardized structure in which to do this generally.

This is great, but the lack of browser support (even for the polyfills) means that it’s going to be quite a while before we can use this everywhere, on the client side.

Interestingly though, if you look at the web components commonly available, many aren’t adding any extra JS for runtime interactivity at all. They’re abstractions to easily construct and render chunks of HTML, using JS for logic in their initial setup (loading data, for example) and rendering. Thus, we could just as well render them server-side. For example:

<qr-code data=”Hi There”></qr-code>

<brick-appbar heading=”App Name”></brick-appbar>

<flag-icon key=”canada”></flag-icon>

<google-map latitude="37.774" longitude="-122.419"></google-map>

<country-select></country-select>

<x-tweet tweetid=”123123"></x-tweet>

Each of these is a chunk of UI. Internally, they’re adding a bunch of divs, or image tags, or a pre-populated select box. From the point of view of the browser though, they’re largely just extra static content (Google Maps does have JavaScript on their end, but to your page it’s just an iframe).

With just a sprinkle of DOM simulation we can move these directly to the server-side. We can take a chunk of HTML like:

then define a few of our own components (more examples in the docs):

and with a quick components.renderPage(html) we serve up our rendered static content:

Want to play with this? Edit it live yourself.

Components internally can do whatever they like: spit out static HTML, go load data from a database or Twitter and render it, use another JavaScript tool or templating library like Mustache.js to generate their content, or use a larger fully-blown framework themselves internally, as necessary.

Even better, they can use Server Components internally too, and you can start to nest abstractions on top of one another. Regardless though, you don’t have to care, you just know you want a <results-paginator> here please, thank you very much.

This works. You can build your web pages with this today: github.com/pimterry/server-components. It’s still an early prototype (v0.2.0 right now), but it’s perfectly usable and remarkably powerful. The main blockers are more common usage patterns, and more advanced features, but those are coming forward quickly as we speak.

As a larger public example, I’ve migrated my personal site onto it, building Tim.FYI entirely on top of Server Components as a test case. This composes together a whole range of generic UI elements with feeds of content from Twitter, Github, Medium, Stack Overflow, and a big bunch more.

It’s all built declaratively, and with data all easily retrieved and cached between requests server-side; being on a server makes things like that trivial. There’s a little client-side JS used for interactivity and polish on top, but it’s purely decorative; the core experience is pure static HTML.

Take a look through the full source at github.com/pimterry/tim.fyi (especially the top-level HTML, teeny-weeny server code, and the component sources) to get a feel for what this looks like in reality.

This is the way you too can build sites now. Server Components in still in the very early stages, but it’s up and working, and a remarkably enjoyable tool to build web applications, without build tools, complicated frameworks, and client-side costs. Have a go. Submit fixes and ask questions! Dive in.

That last 10%

I mentioned that this covers 90% of use cases. That are sites where you really do need a web framework, because serious substantial interactivity has to live in your front-end.

If you really are building a Web Application, not just a website, then there are good reasons to move your processing client-side, and get that ultra low-latency responsiveness, extra degree of control, and more complex live page transformations.

Even in many of those cases though Server Components can help, because of course they’re just web components, so they can run client-side too.

The structure is the same, and the API near-identical, so you can render server-side, take your Server Components and drop in Server-Components-For-Web (…name TBC), and build interactive client-side behaviour and logic in too, when you really need to. Server-side by default, with optional extra client-side magic when you need it.

(This part’s still an very early work in progress, but it’s entirely doable, and there’s lots of exciting opportunities coming here soon)

With this, rendering server-side interactive components for the client-side becomes very possible. Still, clearly this doesn’t solve everything, and there’s definitely a last 5% of web applications that truly do need heavy duty frameworks to manage their complexity. Facebook is not going to start using this tomorrow, and that’s the right decision for large applications.

You’re not building Facebook though (probably). Most sites are fundamentally a series of pages, and you don’t need to push a kitchen sink into people’s browsers just to be able to easily put a page of widgets onto the internet. Big players like Apple, Github, Amazon and Stack Overflow are rendering everything statically server-side, and sprinkling on JS for colour. Your project is not that big.

Client-side JS frameworks cover a small and specialized use case, and right now we’re using them for everything. If you’re not truly deep in that niche, try Server Components instead. Keep it simple, keep it fast, and bring in JS weight only when you need it.

But what about Ember Fast Boot and React’s Server Rendering?

While these do help (and are very cool) they’re not really solving the problem at hand here.

React’s server rendering, for example, is an optimisation to help solve the SEO and first-load performance problems that React suffers from. It’s extra complexity on top to deal with the pain of swimming against the stream like this, but it’s still fighting against the current, just in a bigger boat. That’s great, and a great improvement on React alone, but it’s not the problem that Server Components is looking to resolve.

React and friends are complicated to set up and brimming with internal complexity. If you set them up and then add server-side rendering too you’re still left with unnecessarily heavy JS payloads, a huge suite of build steps, and a vastly larger & harder to understand codebase than you need.

If you’re not building huge complex web apps you don’t want all this: you want a clean powerful standalone tool to quickly put together web pages.

That’s what Server Components is. An simple, fast, lightweight tool to build pages for the modern web, without the pain. Give it a go.