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.
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.
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 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 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 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.
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:
{
"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:
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:
OutputHi, new Next.js project
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;
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:
body {
font-family: 'Raleway', sans-serif;
color: #222;
}
Import the base.css
file 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>
// ...
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.
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:
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:
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:
OutputWarning: 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.
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
:
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:
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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Thank you for taking the time to update these tutorials. That is tremendously helpful, especially to new people needing to stay on track. It also shows integrity and commitment. Well written and clear as well. Thank you.
Hello, I would like to notice that the images aren’t available in this blog, however It’s not really necessary to understand what’s the point of this post, It would be much nicer to have this images available.