Build an SEO-friendly head component for NextJS/React

 laptop with analytics
(Image credit: Negative Space on Pexels)

While React is a powerful JavaScript library, it does not include all of the pieces you need to build a simple, functioning website layout. NextJS is a React framework that lets you build server-rendered applications and websites with ease. 

NextJS also includes a number of tools and features right out of the box like Webpack, Babel, dynamic routing, and prefetching. Most importantly, I’ve found that NextJS is very SEO-friendly.

This framework allows you to utilise server-side rendering, which not only makes your apps and websites load considerably faster, but also makes your React websites much easier for search engines to crawl. 

NextJS also makes it easy to create a well-segmented site architecture by using dynamic routing or custom server routing. For example, you can easily segment your website into different silos like /articles/, /products/, and /services/ for better content structuring. 

Best of all, you can utilise all of the things that make React great like components, component properties and component states to implement really flexible on-page optimisation techniques. In this article, I’m going to detail building a well-optimised head component for React. 

The importance of the head element for SEO

Now before I get into how to build this head component, let’s first talk about why it’s important and what we will be optimising. 

The head element on your website is going to be one of the most important sections when it comes to technical SEO. For one, all of the website metadata is set in the head. These tags include title, meta description, page keywords, any relevant author information and viewport settings. 

The head element is also responsible for setting other important tags like your canonical URL tag, any relevant Facebook OpenGraph Tags (OG tags) or Twitter Cards, and your meta robots tags. Each one of these tags is responsible for conveying different information to Google or social networks so that they can better understand, index and share your content.

Having improperly configured metadata on your website can be catastrophic to your overall website optimisation and can definitely cause your rankings to take a nosedive

Having improperly configured metadata on your website can be catastrophic to your overall website optimisation and can definitely cause your rankings to take a nosedive.  

For example, two of the worst penalties that your website could face from an on-page optimisation standpoint are a 'duplicate title tag' penalty and a 'duplicate meta tag' penalty. These two tags are responsible for giving your website’s 'elevator pitch' to Google. They also dictate the text that a user will see when your website shows up in Google search results. 

If every page on your website has the same exact title and the same exact description set, Google will have a tough time understanding what your website is about. As a result, Google is not going to pay too much attention to your website and it definitely won’t consider it an authority property. 

If you’re interested in learning more about technical on-page SEO, see more on my approach at SpeckyBoy

Now that you understand a little bit more about the head element and why it’s so important under the hood, let’s take a look at how to build an SEO-friendly head component for React. 

 Build an SEO-friendly head component 

The SEO-friendly head component that I detail in this article is specific to NextJS. However, if you are using a different React framework or are just using React, you can use React Helmet in place of the NextJS head component. 

The first thing you’ll want to do is set up your basic head structure. The code below can serve as an example, but feel free to add or remove things to it as you see fit.  At the bare minimum though, your head should include a title tag, meta description tag, canonical URL, and any relevant social tags.

<head>
<title></title>
<meta name="description" content="" />
<meta property="og:type" content="website" />
<meta name="og:title" property="og:title" content="" />
<meta name="og:description" property="og:description" content="" />
<meta property="og:site_name" content="" />
<meta property="og:url" content="" />  
<meta name="twitter:card" content="summary" /> 
<meta name="twitter:title" content="" />
<meta name="twitter:description" content={props.desc} />
<meta name="twitter:site" content="" />
<meta name="twitter:creator" content="" />
<link rel="icon" type="image/png" href="/static/images/favicon.ico" />
<link rel="apple-touch-icon" href="/static/images/favicon.ico" />
<link rel="stylesheet" href="" />
<meta property="og:image" content="" />  
<meta name="twitter:image" content="" />   
<link rel="canonical" href="" />
<script type="text/javascript" src="" ></script>
</head>

 Set up the component 

Next, you’ll want to create a new partial file for your component. You can call the partial file seo-meta.js or similar.  This file should be kept in the partials directory.

Your starting component will look something like this:

import Head from 'next/head'
const Meta = (props) => (      
<Head>
<title></title>
<meta name="description" content="" />
<meta property="og:type" content="website" />
<meta name="og:title" property="og:title" content="" />
<meta name="og:description" property="og:description" content="" />
<meta property="og:site_name" content="" />
<meta property="og:url" content="" />  
<meta name="twitter:card" content="summary" /> 
<meta name="twitter:title" content="" />
<meta name="twitter:description" content={props.desc} />
<meta name="twitter:site" content="" />
<meta name="twitter:creator" content="" />
<link rel="icon" type="image/png" href="/static/images/favicon.ico" />
<link rel="apple-touch-icon" href="/static/images/favicon.ico" />
<link rel="stylesheet" href="" />
<meta property="og:image" content="" />  
<meta name="twitter:image" content="" />   
<link rel="canonical" href="" />
<script type="text/javascript" src="" ></script>
</Head>
)
export default Meta

You’ll notice that I am passing props, or properties, to my component.  We’ll be using these properties to populate our meta tags.

 Import the component 

Once you’ve set up your basic component, you can then import it into your pages. You can import the component by including the following at the top of your page.

import Meta from '../partials/seo-meta.js'

You can now place the meta component within your render function, just like you would with the native NextJS head component.  

Create the component properties

Now that you’ve imported and placed your Meta component you’ll want to set up the properties you’ll need for your metadata. You should typically include title, description, and URL for every page, but you can also include images and other data as necessary. 

You may also want to include properties for CSS and JavaScript files so that you can conditionally load them on pages as necessary.  With all of those properties set, your component would look something like this:

<Meta 
title=“This Is A Title | Website Name” 
desc=“This is the description”
canonical=“https://www.someurl.com”
css='/static/css/styles.css'
js='/static/js/scripts.js'
/> 

If you are just building a static website with NextJS, you should be able to populate the properties with static content. However, if you’re loading pages from dynamic routes and populating the page templates with dynamic code, you’ll want to set these properties dynamically. 

Our website, Proper Noun, uses the WordPress API as a data source, but you can use the instructions below using pretty much any REST or GraphQL API.

When requesting the page, you’ll want to grab and return the relevant metadata and page data during the getInitialProps() async function.  This will then let you use the dynamic data within the render function so that Google and other robots can crawl the information. 

Depending on your data source and a few other factors, you may take a different approach to get the initial properties, but here is a basic approach to get you started.

static async getInitialProps(ctx) {    
const res = await fetch('//api.some-url.com/case_studies/?slug=' + ctx.req.params.slug)
const error_code = res.statusCode > 200 ? res.statusCode : false;
const data = await res.json(); 
let url = 'https://' + ctx.req.headers.host + '/' + ctx.req.params.slug
let meta_title = await data[0].meta_title 
let meta_desc = await data[0].meta_desc
return {
error_code,
   case_study: data,
   meta_title: meta_title,
   meta_desc: meta_desc,
   url: url  
}
}

You will now be able to access the properties you set within your render function. When used in your code they would look something like this.

<Meta 
title={ this.props.meta_title } 
desc={ this.props.meta_desc } 
canonical={this.props.url}
css='/static/css/styles.css'
js='/static/js/scripts.js'
/> 

 Use the properties in your component 

The final step is to set up your component so that it uses the properties it’s being passed. While some of the properties like title and description will be set on every page, others like CSS and JS may be conditional. You’ll want to take this into consideration in your component. 

In our example, we’re passing our properties to the component using the argument props. We can then access the individual props using their names, for example, props.title or props.desc

When setting the conditional blocks, you can take an approach like this:

{
props.css &&
<link rel="stylesheet" href={`${props.css}`}/>
}

This way, if there is no CSS set, you will not set an empty link tag on your page. You can use this same approach for the JavaScript files. 

Once you’ve finished populating your head component it should look something like this:

import Head from 'next/head'
const Meta = (props) => (      
<Head>
<title>{props.title}</title>
<meta name="description" content={props.desc} />
<meta property="og:type" content="website" />
<meta name="og:title" property="og:title" content={props.title} />
<meta name="og:description" property="og:description" content={props.desc} />
<meta property="og:site_name" content="Proper Noun" />
<meta property="og:url" content={`${props.canonical}`} />  
<meta name="twitter:card" content="summary" /> 
<meta name="twitter:title" content={props.title} />
<meta name="twitter:description" content={props.desc} />
<meta name="twitter:site" content="@propernounco" />
<meta name="twitter:creator" content="@propernounco" />
<link rel="icon" type="image/png" href="/static/images/favicon.ico" />
<link rel="apple-touch-icon" href="/static/images/favicon.ico" />
{
props.css &&
<link rel="stylesheet" href={`${props.css}`}/>
}
{
props.image ? (
<meta property="og:image" content={`${props.image}`} />  
) : (
<meta property="og:image" content="https://www.propernoun.co/static/images/proper-noun-social.png" />  
)   
} 
{
props.image &&   
<meta name="twitter:image" content={`${props.image}`} />   
}
{
props.canonical &&
<link rel="canonical" href={`${props.canonical}`} />
}
{
props.js &&   
<script type="text/javascript" src={`${props.js}`}></script>
}
</Head>
)
export default Meta

Read more: