Server side rendering Styled-Components with NextJS
Using ServerStyleSheets in five minutes
I switched to styled-components for most of my projects nearly a year now but never used it with Next.js until recently. This might be a bit late to the party but I feel it’s definitely worth sharing the neat trick of ServerStyleSheets.
🔖 TL;DR: You can find my example repo here 😊
How does it work?
Styled-components supports concurrent SSR (server side rendering) with stylesheet rehydration. The basic idea is that when your app renders on the server, you can create a ServerStyleSheet and add a provider to your React tree which accepts styles via a context API. This doesn’t interfere with global styles, such as keyframes or createGlobalStyle and allows you to use styled-components with React DOM’s various SSR APIs.
In Next.js,<Document />
wraps the <html>
, <body>
, <head>
tags and runs them through a renderPage
method which synchronously renders on the server side. We can override the default<Document/>
by adding a _document.js
file in pages
folder to inject the server side rendered styles into the <head>
.
That’s pretty neat, huh!
Getting Started
Make sure you have these packages in package.json
:
{
"dependencies": {
"next": "latest",
"react": "^16.8.0",
"react-dom": "^16.8.0",
"styled-components": "latest"
},
"devDependencies": {
"babel-plugin-styled-components": "latest",
}
And in .babelrc
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
Now, feel free to add your styles to ./pages/index.js
. For example, let’s add simple GlobalStyle for the heading and Styled.div for the container:
import Head from 'next/head';
import styled, { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
h1 {
font-size: 4rem;
}
`;
const Container = styled.div`
text-align: center;
`;
export default function Home() {
return (
<>
<Head>
<title>SSR styled-components with Next.js Starter</title>
</Head>
<Container>
<GlobalStyle />
<h1>Hello, world!</h1>
</Container>
</>
);
}
Finally, let’s take a look at _document.js
: this is where the magic happens.
Styled-components
creates an instance of ServerStyleSheet
This stylesheet retrieves any styles found in all the components inside our <App />
. This then gets passed into our Html
template later on.
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
sheets.collectStyles
collects all of the styles from the app’s components.sheets.getElement()
generates the style tag and you need to return it as props calledstyles
Testing the app
To view it locally, run npm run dev
then visit http://localhost:3000
If you disable JavaScript on the browser (e.g in Chrome: Settings / Site settings / JavaScript / Blocked), you should still be able to see the styling applied to headings and container, even though the JavaScript didn’t run locally (see the screenshot below).
That’s it!
This is a quick walkthrough to explain how to render server-side styled-components works with Next.js. The steps are pretty straight forward and easy to build on once you have the basics in place.
I remember in the past pulling my hair out to get styles working the way I wanted on the server side. Next.js and the support of styled-components are proving a really powerful tool to make this much simpler to achieve.
Hopefully this tutorial helps to ease some headaches for you! 😃