How to setup Next.js, Apollo GraphQL, TypeScript, Styled Components and Server side rendering in 4 simple Steps#

warning
This article was published a long time ago. Its content may be outdated and no longer reflects the current state of technology.

In this Article I will show you how to create a frontend with many of the most powerful web technologies that are there right now.

We will be creating a simple job list with data provided by graphql.jobs.

Before we start let’s get a little bit more familiar with the tech stack we are going to use.

TypeScript is an open-source programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript, and adds optional static typing to the language.

Next.js is a production ready web framework for creating server side rendered react frontends.

Apollo GrpahQL is Apollos implementation of GraphQL, an open-source data query and manipulation language for APIs.

Styled Components are one of the new ways to use CSS in modern JavaScript. It is the meant to be a successor of CSS Modules, a way to write CSS that’s scoped to a single component, and not leak to any other element in the page.

What is Server Side Rendering or SSR?#

Sever Side Rendering is a technique where you render a client side application (e.g. a single page react app) on the server and ship the fully loaded DOM to the client.

This technique gives your page grater SEO power and performance than a traditional single page application (SPA) since clients (web browsers and crawlers) don’t need to execute JavaScript to get a full view of the DOM.

info
I will use Yarn through out this tutorial. If you are using NPM feel free to replace yarn add with npm install --save.

Step One: Setup Next.js#

First, let’s create a new project:

mkdir killer-frontend
cd killer-frontend
yarn init -y
yarn add react react-dom next
mkdir pages

Now, open the package.json file in the root of your new project and add the following scripts section:

{
"name": "killer-frontend",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"next": "^9.1.6",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}

We can now use the commands yarn dev, yarn build and yarn start.

In development you probably always want to use yarn dev, since it starts your project in the development mode with pre-enabled hot reload.

In production you first have to generate a production build with yarn build and then let it be served by Next.js with yarn start. This method takes more time but will grant you much greater performance.

Let’s run yarn dev and visit http://localhost:3000/. You should see a 404, since we don’t have any pages yet. Don’t worry, we will change that soon.

Screenshot 1

Step Two: Add TypeScript#

I know, this step sound like pain, but adding TypeScript to a Next.js project is incredibly straight forward.

Stop your Next.js dev server and create an empty tsconfig.json file.

touch tsconfig.json

Then start the dev server. Next.js will now recognize that you want to use TypeScript and tells you what to do next.

Screenshot 2

Let’s follow the instructions and add the missing packages:

yarn add --dev typescript @types/react @types/node

Note the --dev flag to install these packages as devdependencies.

We are ready to go! Let’s create a simple Homepage. Go ahead and create a index.tsx file in the /pages directory:

touch pages/index.tsx

And:

import React from "react"
const Index = () => (
<div>
<h1>GraphQL Job Board</h1>
<p>A list of open GraphQL jobs.</p>
</div>
)
export default Index

Start your server and visit http://localhost:3000/.

Screenshot 3

There you have it. Your first page! 🍻

Step Three: Setup Styled Components#

To setup Styled Components we first have to add some packages:

yarn add styled-components
yarn add --dev @types/styled-components babel-plugin-styled-components

Since Next.js uses babel internally we have to configure babel to extract and transpile our Styled Components template code from our components.

Therefore we have to create a .babelrc file in our project root.

touch .babelrc

Then add the following babel config:

{
"presets": [
"next/babel"
],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
]
}

This babel plugin adds a unique identifier to each styled component to avoid checksum mismatches. This is great for stability but could get uns into warning-hell, since it could happen that the same class gets a different name on the server than on the client. Fortunately we can avoid this problem by setting ssr to true.

The displayName option adds the component name to the generated CSS class to make it easier for debugging.

preprocess is an experimental feature that we don’t need in our case.

Now we are ready to render our components on the server side. In order to do that we have to create a _document.tsx file in the pages/ directory.

Feel free to read more about Next.js magic files.

touch pages/_document.tsx

And add the code that will handle the server sided rendering of our styled components:

_document.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
import React from "react"
import Document, { Head, Main, NextScript } from "next/document"
import { ServerStyleSheet } from "styled-components"
export default class MyDocument extends Document<any> {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
// wraps the collectStyles provider around our <App />.
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
})
// extract the initial props that may be present.
const initialProps = await Document.getInitialProps(ctx)
// returning the original props together with our styled components.
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
render() {
return (
<html>
<Head>
{this.props.styleTags /*rendering the actually stylesheet*/}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}

Let’s create a global style to test our configuration:

_app.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142
import React from "react"
import App from "next/app"
import Head from "next/head"
import { ThemeProvider, createGlobalStyle } from "styled-components"
export interface ITheme {
niceBlack: string;
}
export interface IThemeWrapper {
theme: ITheme;
}
export const theme: ITheme = {
niceBlack: "#001F3F",
}
const GlobalStyle = createGlobalStyle<IThemeWrapper>`
body {
margin: 0 auto;
color: ${props => props.theme.niceBlack};
}
`
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<React.Fragment>
<Head>
<title>GraphQL Job Board</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
)
}
}

If we restart our server we can verify that the stylesheet is being rendered on the server side:

Screenshot 4

And there we go. You have successfully implemented Styled Components with SSR! 👌

Step Four: Setup Apollo GraphQL#

I will assume that you know the fundamentals of GraphQL. If not, I highly recommend you reading this article.

We will be using Apollos implementation of GraphQL since it’s well maintained and battle proofed.

The concept#

In order to keep our code quality clean we will be writing our queries and mutations in the pages of our Next.js project only and pass the data we need down to our components as props.

We will use the useQuery hook from the @apollo/react-hooks package to write our queries. This makes implementing server side rendering a no-brainer and works very well in our react context.

If a client (e.g web browser) makes a GET request to one of our pages the idea is that we will evaluate all queries that need to be run on that page and fire them on the server side. We take the data returned by those queries and re hydrate the DOM with it. Then we send the fully re hydrated DOM back to the client.

Since this isn’t a GraphQL Server tutorial we will be using a public GraphQL endpoint to have some sample data. In our case this will be https://api.graphql.jobs/.

The implementation#

Let’s start by installing all the packages we need:

yarn add @apollo/react-hooks apollo-boost graphql node-fetch next-with-apollo

next-with-apollo is a provider that will handle the re hydration in our Next.js context. If you want you could create your own implementation but there is no need to reinvent the wheel ;)

We can now create our Apollo client:

hooks/withApollo.ts
12345678910
import withApollo from "next-with-apollo"
import ApolloClient, { InMemoryCache } from "apollo-boost"
export default withApollo(
({ initialState }) =>
new ApolloClient({
uri: "https://api.graphql.jobs/",
cache: new InMemoryCache().restore(initialState || {})
})
)

initialState is the accumulated data returned by the queries. It will be passed to the cache as the initial data.

After that we have to make some additions to the _app.tsx file to add our own withApollo provider:

_app.tsx
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
import React from "react"
import App from "next/app"
import Head from "next/head"
import { ThemeProvider, createGlobalStyle } from "styled-components"
import { ApolloProvider } from "@apollo/react-hooks"
import withApollo from "../hooks/withApollo"
import { ApolloClient, NormalizedCacheObject } from "apollo-boost"
export interface ITheme {
niceBlack: string;
}
export interface IThemeWrapper {
theme: ITheme;
}
export const theme: ITheme = {
niceBlack: "#001F3F",
}
const GlobalStyle = createGlobalStyle<IThemeWrapper>`
body {
margin: 0 auto;
color: ${props => props.theme.niceBlack};
}
`
// since "apollo" isn't a native Next.js prop we have to declare it's type.
interface IProps {
apollo: ApolloClient<NormalizedCacheObject>;
}
// adds our custom props interface to the generic App base class.
class MyApp extends App<IProps> {
render() {
// instead of creating a client here, we use the rehydrated apollo client provided by our own withApollo provider.
const { Component, pageProps, apollo } = this.props
return (
<React.Fragment>
<Head>
<title>GraphQL Job Board</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
{/* adds the apollo provider to provide it's children with the apollo scope. */}
<ApolloProvider client={apollo}>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>
</ApolloProvider>
</React.Fragment>
)
}
}
// before exporting our App we wrapp it with our own withApollo provider to have access to the our rehydrated apollo client.
export default withApollo(MyApp)

All changes are marked with comments.

We can now write a query to get all jobs from our endpoint. The best place to do this is in the pages/index.tsx file:

index.tsx
123456789101112131415161718192021222324252627282930313233
import React from "react"
import JobList from "../components/JobList"
import { useQuery } from "@apollo/react-hooks"
import { gql } from "apollo-boost"
const Index = () => {
// our query that defines the attributes we want to get.
const JOBS_QUERY = gql`
query {
jobs {
id
title
applyUrl
company {
name
}
}
}
`
// the hook that calls the query.
const jobs = useQuery(JOBS_QUERY)
return (
<div>
<h1>GraphQL Job Board</h1>
<p>A list of open GraphQL jobs.</p>
<JobList jobs={jobs?.data?.jobs || []} />
</div>
)
}
export default Index

As you can see we pass the returned jobs or an empty array as a prop to our JobList component since jobs.data.jobs isn’t always defined.

Tip: “?” is a safe navigation operator. We have to use it here because jobs.data can be undefined at a certain point in time.

The last thing left to do is to build our JobList component:

JobList.tsx
import React from "react"
import styled from "styled-components"
// this interface defines the shape of the data returned by the jobs query.
export interface IJob {
id: string;
applyUrl: string;
title: string;
company: {
name: string;
}
}
interface IProps {
jobs: IJob[];
}
const List = styled.ul``
const ListItem = styled.li`
margin-bottom: .5rem;
`
const JobList = ({ jobs }: IProps) => {
const listItems = jobs.map((job) => {
return (
<ListItem key={job.id}>
{job.title} by {job.company.name} [<a href={job.applyUrl} target="_blank">Apply</a>]
</ListItem>
)
})
return (
<List>
{listItems}
</List>
)
}
export default JobList

And here is the final result:

Screenshot 5

If we take a look at the source code we can see that everything was re hydrated on the server side:

Screenshot 6

And that’s it. You have created a powerful frontend with many of the most powerful web technologies that are on the market right now. 🎉

Source code: https://github.com/maxbause/next-graphql-styled-components-ts-boilerplate

Cheers, Max.