Improve your React.js SEO with NextJS

By Ronak Gothi On September 27 2021

In React everything is rendered on the client-side, this is great for UX, but when the web-crawlers tries to crawl your site, it wouldn't find any content on the DOM. This is bad in terms of SEO because crawlers cannot process the API response and wouldn't wait for DOM re-render.

Table of contents

  1. How browsers, crawlers and social media platforms process your content?
    • Server-Side rendering
    • Client-Side Rendering
  2. How is React in terms of SEO?
  3. How does NextJS solves the biggest problem of React?
  4. Next.js methods you can use to render these pages for better SEO optimisation?
    • getStaticProps and getStaticPaths
    • getServerSideProps

How do browsers, crawlers and social media platforms process your content?

There are two ways web content can be generated and then processed by the browser

  1. Server-side rendering
  2. Client-side rendering
Server-Side rendering

The HTML contents is generated at the server side and the browser then paints the generated HTML. The meta tags that are generated during the server-side rendering is available at initial load. So when your page loads, the meta tags present at the initial load is easily processed by the web crawling for content ranking.

Client-Side Rendering

The HTML contents are rendered dynamically through javascript and it is injected into the DOM tree. This takes a bit of a time to process and thus is not crawler friendly because crawler would then need to dynamically render content too.

From the above two ways, it's clear that Client-Side Rendering does not go well with the web crawlers, and thus is bad in terms of SEO.

How is React in terms of SEO?

React is a Client-Side rendering framework, So the contents are loaded after the initial page load and when crawlers crawl the page they cannot find the routes or the meta tags that are loaded dynamically.

How does NextJS solves the biggest problem of React?

Next.js is an opinionated framework built on top of React.js. It introduces some advancements on things that traditional client-side rendering library misses, as well as keeps the good things from ReactJS.

Few features of NextJS are

  • Static routing (even for dynamic content).
  • Server-side rendering of pages.
  • Caching of SSR pages.

The server-side rendering helps search engines, and thus SEO friendly while also building reusable components on React.

Next.js methods you can use to render these pages for better SEO optimisation?

SSR is possible in Next.js. We could use one of the 2 ways based on our usecase.

  1. getStaticProps and getStaticPaths
  2. getServerSideProps
getStaticProps and getStaticPath

getStaticProps and getStaticPath will get you the data before the page renders through this we can set the meta tags and og tags needed for SEO optimisation.

Let's 2 API endpoints.

1
2
3
4
5
6
7
8
9
10
//Filename: /main/pages/api/user/[id]/index.js

const sampleData = {
  0: { name: 'Hoo', desc: 'He is doctor' },
  1: { name: 'John', desc: 'He is engineer' },
  2: { name: 'Doe', desc: 'He is doctor' },
}
export default async function handler(req, res) {
  res.status(200).json(sampleData[req.query.id])
}

The above gets user data based on dynamic user id

1
2
3
4
5
//Filename: main/pages/api/user/index.js

export default async function handler(req, res) {
  res.status(200).json({ name: 'John Doe', desc: 'He is a doctor' })
}

The above gets static user content.

As you can see in the file name above we are using [id] square brackets which is then accessible as req.query.id. With that id, we can fetch users from a database or in our case we are going to use the above sample data.

Now that everything is set up for the data side, let’s move on to rendering pages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Filename: main/pages/user/[id]/index.js
import Head from 'next/head'
import Axios from 'axios';

export default function Home({ data }) {
  return (
    <>
      <Head>
        <title>{data.name}</title>
        <meta name='description' content={data.desc} />
      </Head>
      <div>Data</div>
    </>
  )
}
export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking',
  };
}

export async function getStaticProps({ params }) {
  const { data } = await Axios.get(`http://localhost:3000/api/user/${params.id}`);
  if (data.status >= 300) {
    return {
      notFound: true
    }
  }
  return {
    props: {
      data
    },
    revalidate: 100,
  }
} 

The above code is an example of dynamic routing based on dynamic content. The route user/[id]/ would generate dynamic content. params.id in the function getStaticProps would fetch the data at the server side before rendering the React Components.

getStaticPaths should be coupled with getStaticProps so that this method works, as you can see fallback: 'blocking' which prevents page load before the server-side operation is completed.

The getStaticProps will handle the data fetching operations and would also cache the page so that subsequent page request is served via cache.

The below code will redirect to the 404 pages if no data is found for that user:

1
2
3
4
5
if (data.status >= 300) {
  return {  
    notFound: true  
  }
}

revalidate: 100 This will cache the data for 100 second, post 100 seconds, in case the data is changed in the backend a fresh page would be regenerated.

getServerSideProps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import Axios from 'axios';

export default function Home({ data }) {
  return (
    <>
    <Head>
      <title>{data.name}</title>
      <meta name='description' content={data.desc} />
    </Head>
    <div>Data</div>
    </>
  )
}

export async function getServerSideProps({ req, res }) {
  const { data } = await Axios.get('http://localhost:3000/api/user');
  return {
    props: {
      data
    },
  }
}

getServerSideProps fetches the data on page request, this would pre-render the page on every request and is likely slower than getStaticProps. The generated page content is not cached and thus must be used when the content of the page is to be generated afresh on each request.

1
2
3
4
5
6
7
8
export async function getServerSideProps({ req, res }) {  
  const { data } = await Axios.get('http://localhost:3000/api/user');  
  return {  
    props: {  
      data  
    },  
  }  
}

The above function will fetch the data and send it to the component as props. revalidate parameter is not being used with serverSideProps as the page would generated a fresh on each request.

You can find the project here