Rendering CSS on Servers for Next.js (React) Apps

Chris Nwamba
👁️ 1,834 views
💬 comments

Server-side rendering is not such an easy task. Its ambiguity is independent of the library — it’s just generally daunting. This is why some amazing developers are out there building open source tools like Next.js (React) and Nuxt.js(Vue) to help simplify the process of rendering your app views to a server. I have written previously on Next.js so you can get started with the short article if you haven’t used Next.js before.

While working with Next.js, I ran into an obscure problem. This had to do with how styles are rendered on the server, and it never struck till now that styles are also first class citizens for server-side rendering.

In this article, I will describe these challenges of rendering CSS on the server and then show you how we can overcome the challenges.

Style Patterns in React

There are few common ways to write CSS in React which all work. Some of these patters are widely used, some are frowned upon, while some are fairly used. Depending on your situation, you are applying styles to your React app in one of the following ways:

  1. Global Styles: This is the pattern we are used to practicing. It’s common in the web ecosystem generally, not just React. Basically, you have a local or CDN stylesheet included in your HTML page using the link tag:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />

This is the less preferred styling pattern because it does not encourage component reuse, neither does it encourage style composition. That global styles are discouraged doesn’t imply they are completely useless. There are cases like font inclusion and CSS resets/defaults inclusion where you would need to include styles globally.

  1. Inline Styles: These styles are applied to DOM elements or React components using the React style property. The implementation much like the HTML inline style attribute but uses the JavaScript element.style API. Here is an example:
const titleStyle = {
  fontSize: '4rem';
  lineHeight: '1.6';
  color: '#222'
}

<h1 style={titleStyle} {...props}>{props.children}<h1>

This pattern encourages style and component composition as well as reuse. But it’s a bit hard to write style like this. Think of handling hover or focus state with pseudo selectors. You will have managed a lot of JavaScript logic across your project just to do that.

  1. Component Styles: Component styles encourage reuse and help you to compose styles and components better. You need a utility or library to help you accomplish this. Style loaders, styled-components, Glamor, etc are

Styled Components

In the three patterns I mentioned, styled-components are most favored. They are inline component styles but with more power to do things like complex (pseudo) selection, nesting, etc. They are referred to as CSS in JS which at first might sound awkward, but come to think of it — it’s an amazing concept. Who needs Sass when I can do my calculations, create variables and iterate over data right in JavaScript for a given block of CSS?

To see how to use styled components (or component styles), let’s create a new Next.js (which is React based).

Setup a Next.js Project

Create a project by creating an empty folder on your machine, then run the following command on that folder:

yarn init -y

(Make sure you have Yarn installed)

This will create a package.json file with the following content:

{
  "name": "css-ssr-next",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

Install Next.js, React, and React DOM:

yarn add next react react-dom

Then update the package.json to start a Next.js app with dev script:

{
  "name": "css-ssr-next",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "next"
  },
  "dependencies": {
    "next": "^4.2.1",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  }
}

Create a pages folder and add an index.js with the following content:

import React from 'react';
const Index = () => <h1>Hi, new Next.js project</h1>;
export default Index;

Now run the script to start the dev server:

yarn dev

You should get this in your browser at localhost’s port 3000:

Let’s add some styles.

Styles with Styled Components

Let’s use the styled-components library to see how we can style components. First, use the Head component from Next to normalize styles using normalize.css:

import React from 'react';
import Head from 'next/head';

const Index = () =>
  <div>
    <Head>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
      <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
    </Head>
    <h1>Hi, new Next.js project</h1>
  </div>;

export default Index;

I also added a font from Google font. This wouldn’t change anything until the style is applied to the body element. Create a static folder on the root of your Next.js project and add a base.css file with the following CSS content:

body {
  font-family: 'Raleway', sans-serif;
  color: #222;
}

Import the base style in the Index page:

<Head>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.css" />
  <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
  <link rel="stylesheet" href="/static/base.css" />
</Head>

You can see that the font changed and in the browser:

We are done with the dirty global style job. Let's start adding components with composed styles. Install styled-components:

yarn add styled-components

Next, create a components folder and add a Button.js file. Update the file with the following:

import React from 'react';
import styled from 'styled-components';
const ButtonBase = (props) => <button {...props}>{props.children}</button>
const Button = styled(ButtonBase)`
  /* Rectangle 2: */
  background: #0077E2;
  box-shadow: 0 2px 7px 0 rgba(120,137,149,0.25);
  border-radius: 3px;
  text-transform: uppercase;
  padding: 10px;
  color: #fff;
  border: #0077E2;
`
export default Button;

We are using styled-components imported as styled to style a Button. ButtonBase returns a skeleton of the button while Button returns a component with a styled ButtonBase.

Import and use the button in the Index page:

import React from 'react';
import Head from 'next/head';
import Button from '../components/Button'
const Index = () => <div>
  <Head>
    ...
  </Head>
  <h1>Hi, new Next.js project</h1>
  <Button>Clicker</Button>
</div>;
export default Index;

This gives us a fine looking button after hot-reloading the page. How hard-reload the page and see what happens:

Weird, right? The button styles seem to be missing, but somehow, the base styles are intact. Now view the page source to dig what happened:

The blue box shows that the content is rendered to the server, but it doesn’t show any style anywhere on the page relating to the button. On the other hand, the font styles were applied. From what you can see in the red box, the external files were rendered to the server successfully.

So what went wrong with the component styles? Take a look at the console:

You can see an error showing a miss-match in the class name. This is because, when you reload, content is first fetched from the server. Unfortunately, styled-components are not rendered to the server which makes them unavailable at that time. Let’s take a look at a solution.

Styled JSX

The team working on Next.js introduced a library called styled jsx to help control mitigate this problem. The library ensures that styles you write are rendered on the server as well as the browser and not just the browser alone.

It already comes bundled with Next.js, so you don’t need to install. Just update the Button component to make use of it:

import React from 'react';

const Button = props => (
  <button {...props}>
    {props.children}
    <style jsx>{`
      background: #0077e2;
      box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
      border-radius: 3px;
      text-transform: uppercase;
      padding: 10px;
      color: #fff;
      border: #0077e2;
    `}</style>
  </button>
);
export default Button;

Before the closing tag of the root element in a component we want to style, you can create a style element with the jsx attribute. In the style element, open a curly brace that contains template strings. The strings should be valid CSS styles and server as your component style. No matter how hard you reload this time, your style stays put:

The blue box in the image above shows that the button style is now rendered to the server as well.

You can also use selectors in styled jsx if your component is not composed of only one element:

(
<div {...props}>
<h1 className="title">Hi Title</h1>
<button>Clicker</button>
    <style jsx>{`
      h1.title {
        color: #222
      }
      button {
        background: #0077e2;
        box-shadow: 0 2px 7px 0 rgba(120, 137, 149, 0.25);
        border-radius: 3px;
        text-transform: uppercase;
        padding: 10px;
        color: #fff;
        border: #0077e2;
      }
    `}</style>
</div>
)

And that’s how you render styles to a server in Next.js projects. You can learn more about styled jsx on the project’s repository.

Chris Nwamba

91 posts

JavaScript Preacher. Building the web with the JS community.