Components: Server-Side vs. Client-Side

Avatar of Sean C Davis
Sean C Davis on

Take your JavaScript to the next level at Frontend Masters.

Building a website in 2021? I’m guessing you’re going to take a component-driven approach. It’s all the chatter these days. React and Vue are everywhere (is Angular still a thing?), while other emerging frameworks continue to attempt a push into the spotlight.

Over the last decade or so we’ve seen an explosion of frameworks and tools that help us build sites systematically using components. Early frameworks like AngularJS helped shape the generic concept of web components. Web components are also reusable bits of HTML code that are written in JavaScript and made functional by the browser. They are client-side components.

But components, in a more generic sense, have actually been around much longer. In fact, they go back to the early days of the web. They just haven’t typically been called components, though they still function as such. Server components are also reusable bits of code, but are compiled into HTML before the browser sees them. They are server-side components, and they are still very much a thing today.

Even in a world in which all it seems like we hear is “React, React, React,” both types of components are still relevant and can help us build super awesome websites. Let’s explore how client and server components differ from one another. That will give us a clearer picture of where we came from. And then we’ll have the information we need to dream about the future.

Rendering

Perhaps the biggest difference between client-side and server-side components is what makes them what they are. That is the thing that is responsible for rendering them.

Server components are rendered by — you guessed it! — the server. They aren’t typically referred to as components. They’re often called partials, includes, snippets, or templates, depending on the framework in which they are used.

Server components can take two flavors. The first is the classic approach, which is to render components in real-time based on a request from the client. See here:

Illustrated diagram of a browser making a request to an app server that requests components, which compile to a page and are returned to the app server to go back to the browser.
Server-side rendered components

The second flavor is the Jamstack approach. In this case, the entire site is compiled during a build a process, and static HTML is already available when requested by the client. See here:

An illustrated diagram showing the browser requests a page that has already been created, which consists of components that have gone through a build process from the server, then returns to the browser.
Server components on a Jamstack site have already been compiled into HTML.

In both cases, the client (i.e. your browser) never sees the distinction between your components. It simply receives a bunch of HTML from the server.

Client components, on the other hand, are rendered by — you are two-for-two and on a ROLL! — the client. They are written in JavaScript and rendered by the client (your browser). Because the server is the server and it knows all, it can know about your client components, but whether it cares enough to do anything with them depends on the framework you’re using.

Like server components, there are also two flavors of client components. The first is the more official web component, which makes use of the shadow DOM. The shadow DOM helps with encapsulating styles and other functionality (we’ll talk more about this later). Frameworks like Polymer and Stencil make use of the shadow DOM.

The more popular frameworks, like React and Vue, represent the second flavor of component, which handles DOM manipulation and scoping on their own.

Interactivity

Because server components are just HTML when they are sent to the client, if they are to be interactive on the front end, the application must load JavaScript code separately.

Consider a countdown timer. Its presentation is determined by HTML and CSS (we‘ll come back to the CSS part). But if it is to do its thing (count), it also needs some JavaScript. That means not just bringing in that JavaScript, but also having a means by which the JavaScript can attach itself to the countdown’s HTML element(s), which must either be done manually or with (yet) another framework.

An illustrated diagram showing the browser requesting both a JavaScript bundler containing the JavaScript for the component, and a page with the component's markup that both are returned to the browser.
A component’s HTML and JavaScript are separated in SSR components.

Though this may feel unnecessarily tedious (especially if you’ve been around long enough to have been forced into this approach), there are benefits to it. It is a clear separation of concerns, where server-side code lives in one place, while the functionality lives in another. And it brings only the code it needs for the interactivity (theoretically), which can lessen the burden on the browser.

With client components, the markup and interactivity tend to be tightly coupled, often in the same file or directory. While this can quickly become a mess if you’re not diligent about staying organized, one major benefit to client components is that they already have access to the client. And because they are written in JavaScript, their functionality can ship right alongside their markup (and styles).

A diagram showing the same flow as before, but both the component markup and JavaScript are included in the bundler, which then renders on the page.
Client-side components are all wrapped up in JavaScript code.

Performance

In a one-to-one comparison, server-side components tend to perform better. When the page that a browser receives contains everything it needs for presentation, it’s going to be able to deliver that presentation to the user much quicker.

Diagram showing the browser asking for a page of HTML in a single request.
Technically all you need when rendering SSR components is a single request.

Because client-side components require JavaScript, the browser must download or process additional information (often in separate files) to be able to render the component.

Same diagram as before, but a bundler is requested by the browser along with the page.
Client component often require more code and requests.

That said, client-side components are often used within the context of a larger framework. React has Gatsby and Next, while Vue has Nuxt. These frameworks have mechanisms for creating a superior in-app experience. What I mean is that, while they may be slower to load the first page you visit on a site, they can then focus their energy on delivering subsequent views extremely fast — often faster than a server-side rendered site can deliver its content.

If you’re thinking, Yeah but what about pre-rendering and…

Yes, you’re right. We’ll get there. Also, no more spoilers, please. The rest of us are along for the ride.

Languages

Server components can be written in (almost) any server-side language. This enables you to write your templates in the same language as your application’s logic. For example, applications written with Ruby on Rails use ERB templating by default, which is a form of Ruby. Thus, Rails apps use the same language for the application itself as it does for its components.

The reason client components are written in JavaScript is because that’s the language browsers parse for interactivity on a website. However, JavaScript also has server-based runtimes, the most popular of which is Node.js. That means code for client components could be written in the same language as the application, as long as the application is written with Node (or similar).

Styling (CSS)

When it comes to styling components, server-side components run into the same trouble they face with JavaScript. The styles are typically detached from the components, and require a bit of extra effort to tie styles to the elements on the page.

However, there are frameworks like Tailwind CSS that are working to make this process less painful.

Many client-side component libraries come with CSS support (or at least a pattern for styling) right out of the box. That often means including the styles in the same file as the markup and logic, which can get messy. But typically, with a little effort, you can adjust that approach to your liking.

Welcome to the (hybrid) future

Neither type of component is the answer by itself. Server-side components require additional effort in styling and interactivity that feels unnecessary when we look at the offerings of client components. But then client components have a tendency to take away from performance on the front end. And because the success of a website often depends on user engagement, a lack of performance can hurt the end result and be enough not to want to use client components.

What does that mean for a future that demands both performance and a good developer experience? More than likely, a hybrid approach.

Components are going to have to be rendered on the server side. They just are. That‘s how we optimize performance, and good performance is going to continue to be an attribute of successful websites. But, now that we’ve seen the ease of front-end logic and interactivity using frameworks, again, like React and Vue, those frameworks are here to stay (at least for awhile).

So where are we going?

I think we’re going to see these components come together in three ways in the very near future.

1. Advancement of JavaScript framework frameworks

Remember when you thought up that spoiler about pre-rendering? Well, let’s talk about it now.

Frameworks like Gatsby, Next, and Nuxt act as front-end engines built on top of component frameworks, like React and Vue. They bring together tooling to build a comprehensive front-end experience using their preferred framework. One such feature is pre-rendering, which means these engines will introspect components and then write static HTML on the page while the site is being built. Then, when users view that page, it‘s actually already there. They don’t need JavaScript to view it.

However, JavaScript comes into play through a process called hydration. After the page loads and your user sees all the (static) content, that’s when JavaScript goes to work. It takes over the components to make them interactive. This provides the opportunity to build a client-side, component-based website with some of the benefits of the server, namely performance and SEO.

These tools have gotten super popular because of this approach, and I suspect we’ll see them continue to advance.

2. Baked-in client-side pre-rendering

That’s a lot of compound words.

What I‘ve been thinking about a lot the last couple years is: Why doesn’t React (or Vue) take on server-side rendering? They do, it’s just not super easy to understand or implement without another framework to help.

On one hand, I understand the single-responsibility principle, and that these component frameworks are just ways to build client-side components. But it felt like a huge miss to delegate server-side rendering to bigger, more complex tools like Gatsby and Next (among others).

Well, React has started moving that way. Vue is already there. And Svelte has made this approach a priority from the beginning.

I think we‘re going to see a lot more development while these traditionally client-side-focused tools solve for server-side rendering. I suspect that also means we‘ll hear a little more from Svelte in the future, which seems like it’s ahead of the game in this regard.

That may also lead to the development of more competitors to bulkier tools like Gatsby and Next. For example, look at what Netlify is doing with their website. It‘s an Eleventy project that pulls in Vue components and renders them for use on the server. What it’s missing is the hydration and interactivity piece. I expect that to come together in the very near future.

3. Server-side component interactivity

And still, we can‘t discount the continued use of server-side components. The one side effect of both of the other two advancements is that they’re still using JavaScript frameworks that can feel unnecessary when you only need just a little interactivity.

There must be a simpler way to add just a little JavaScript to make a server-side component that are written in a server-side language more interactive.

Solving that problem seems to be the approach from the folks at Basecamp, who just released Hotwire, which is a means to bring some of the gains of client components to the server, using (almost) any server-side language.

I don‘t know if that means we‘re going to see competition to Hotwire emerge right away. But I do think Hotwire is going to get some attention. And that might just bring folks back to working with full-stack monolithic frameworks like Rails. (Personally, I love that Rails hasn’t become obsolete in this JavaScript-focused world. The more competition we have, the better the web gets.)

Where do you think all this component business is going? Let’s talk about it.