Tutorial

How To Render CSS on React App Servers

JavaScriptReactNext.js

Introduction

Next.js (React) and Nuxt.js (Vue) help streamline the process of rendering your app views to a server.

You will still need a solution for rendering CSS in your React components in a way that accommodates development. You will also need a solution for rendering CSS on the server so the styles are available.

In this article, we will go over the challenges of rendering CSS and then use styled-components and styled-jsx on the server in a Next.js project.

Prerequisites

To complete this tutorial, you’ll need:

This tutorial was verified with Node v16.2.0, npm v7.14.0, react v17.0.2, react-dom v17.0.2, next v10.2.3, and styled-compnents v5.3.0.

Understanding Style Patterns in React

There are few common ways to write CSS in React which all work. Depending on your situation, you are applying styles to your React app in one of the following ways:

Global Styles

Global styles are the pattern of including local or content delivery network (CDN) hosted stylesheets.

Here is an example:

<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. It also does not encourage style composition.

These CSS rules are not scoped directly to specific elements or components. There is a high likelihood of conflicts occurring when components interact in a way that was not anticipated through importing, nesting, or expanding.

However, there are cases like font inclusion and CSS resets and defaults where you could include styles globally.

Inline Styles

Inline styles are directly 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.

However, inline styles does not provide you with a way to handle hover of focus states with pseudo-classes and target pseudo-elements. For larger and complex projects, you may need a solution that is more robust.

Component Styles

Component styles encourage reuse and help you to compose styles and components better. You will need a utility or library to help you accomplish this. Style loaders, styled-components, Glamor, etc. are examples of tools for supporting component styles.

styled-components are a popular solution for implementing CSS-in-JS. They are inline component styles but with more power to do things like complex (pseudo) selection, nesting, etc.

Step 1 – Setting Up the Project

To get started, open your terminal and create a new folder for your project:

  • mkdir css-ssr-next-example

Next, change into the new project directory:

  • cd css-ssr-next-example

Then run the following command to initialize it:

  • npm init -y

This will create a package.json file which you will use to track dependencies.

Then, install Next.js, React, and React DOM:

  • npm install next@10.2.3 react@17.0.2 react-dom17.0.2

At this point, you will have a new project with Next.js, React, and React DOM.

Note: Since publication, a more modern approach to creating a Next.js project could utilize create-next-app.

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

package.json
{
  "name": "css-ssr-next-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "next"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^10.2.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}

Then, create a pages directory:

  • mkdir pages

In this directory, add an index.js file with the following content:

pages/index.js
import React from 'react';

const Index = () => <h1>Hi, new Next.js project</h1>;

export default Index;

Now run the dev script to start the server:

  • npm run dev

If you open localhost:3000 in a web browser, you will observe:

Output
Hi, new Next.js project

First, use the Head component from Next to normalize styles using normalize.css:

pages/index.js
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;

Next, create a static folder on the root of your Next.js project:

  • mkdir static

In this directory, add a base.css file with the following CSS content:

static/base.css
body {
  font-family: 'Raleway', sans-serif;
  color: #222;
}

Import the base.css file in the Index page:

pages/index.js
// ...

    <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>

// ...

In the browser, the font will visibly change from the default font to Raleway.

At this point, you have a Next.js project with global styles.

Step 2 – Using Styled Components

Let’s use the styled-components library to style components.

First, install styled-components:

  • npm install styled-components@5.3.0

Next, create a components directory:

mkdir components

In this directory, add a Button.js file and update the file with the following:

components/Button.js
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 component in the Index page:

index.js
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;

Save the changes and observe the application in a browser. Underneath the text will be a new styled button.

However, after a hard refresh, the button is no longer styled as expected.

The button styles seem to be missing, but somehow, the base styles are intact. Use web developer tools to inspect the page source.

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, as we can observe from the font styles that were applied, we know the external files were rendered to the server successfully.

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

Output
Warning: Prop `className` did not match. Server: "sc-gtsrHT kbmjhF" Client: "sc-bdnxRM kbyRfM"

You can observe 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.

Note: Since publication, styled-components supports server-side rendering.

You can also refer to Next.js’s with-styled-components example.

Let’s take a look at a solution.

Step 3 – Using 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 anything.

Revisit the Button component and modify it to use styled-jsx:

components/Button.js
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.

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

components/TitleAndButton.js
import React from 'react';

const TitleAndButton = props => (<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>);

export default TitleAndButton;

Now, you can import and use the TitleAndButton component in the Index page. Both elements will be styled as expected.

Note: You can also refer to Next.js’s with-styled-jsx example.

styled-jsx is one approach to rendering styles to a server in Next.js projects.

Conclusion

In this article, you learned about the challenges of rendering CSS and then used styled-components and styled-jsx on the server in a Next.js project.

If you’d like to learn more about Next.js, try our Getting Started with Next.js tech talk, or check out our Next.js topic page for exercises and programming projects.

Creative Commons License