Ashutosh Singh Ashutosh is a full-time engineering student, JavaScript developer, and freelance writer. He writes about the fundamentals of JavaScript, Node.js, React, and how to build portfolio projects in JavaScript and React.

Search-optimized SPAs with React Helmet

5 min read 1635

Search-optimized SPAs With React Helmet

Every developer wants their website to appear at the top of search results pages. Unfortunately, search engine crawlers do not yet reliably understand/render JavaScript, which means SPAs built on top of React, Angular, etc. are not generally favored by search engine crawlers. This can be a significant issue for many developers. Server-side rendering (SSR) may solve this problem, but it has its limitations, too.

React Helmet is a document head manager for React. It makes it easy to update meta tags on the server as well as the client, which means this library is the perfect choice for making your apps SEO- and social media-friendly. In this article, we will see how you can add React Helmet to your project and use it.

Setup

Initialize a new React project using create-react-app and start the development server.

npx create-react-app react-helmet-tutorial
cd react-helmet-tutorial
npm start

Unless your system’s port 3000 is occupied, you can head over to http://localhost:3000 and see your app.

Newly Created React App

Like most SPAs, your app has a default <head> element; you can verify this by opening the inspector in your browser by hitting F12 in Chrome or Ctrl+Shift+C in Firefox.

This is what the <head> element looks like:

<head>
    <meta charset="utf-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#000000">
    <meta name="description" content="Web site created using create-react-app">
    <link rel="apple-touch-icon" href="/logo192.png">

    <link rel="manifest" href="/manifest.json">

    <title>React App</title>
  <style type="text/css">
   <!-- CSS -->
  </style>
</head>

One way to manage this <head> is by updating public/index.html, but this may not always be optimal.

Installation and usage

You can install React Helmet into your project using npm or Yarn:

npm install react-helmet
# OR, using Yarn:
yarn add react-helmet

To use React Helmet, you first import the Helmet component, then you add the elements you want in your document’s <head>.

We made a custom demo for .
No really. Click here to check it out.

// src/App.js

import React from "react";
import "./App.css";
import { Helmet } from "react-helmet";

function App() {
  return (
    <div className="App">

      <Helmet>
        <html lang="en" />
        <title>React Helmet Tutorial</title>
        <meta name="description" content="Tutorial for React Helmet" />
        <meta name="theme-color" content="#E6E6FA" />
      </Helmet>

      <header className="App-header">Title will be React Helmet Tutorial</header>
    </div>
  );
}
export default App;

As soon as you update the src/App.js file, you will see the title of the React app change.

In this example, only <title>, <html>, and <meta> elements for description and theme-color are used, but you can use other elements such as title, base, meta, link, script, noscript, and style as children of Helmet.

You can also set attributes for body and html tags. For example:

 <Helmet>
        {/* html attributes */}
        <html lang="en" />

        {/* body attributes */}
        <body className="dark" />

        {/* title element */}
        <title>React Helmet Tutorial</title>

        {/* base element */}
        <base target="_blank" href="https://blog.logrocket.com/" />

        {/* meta elements */}
        <meta name="description" content="Tutorial for React Helmet" />

        <meta name="theme-color" content="#E6E6FA" />

        {/* link elements */}
        <link rel="canonical" href="https://blog.logrocket.com/" />
</Helmet>

Who gets preference: Child or parent?

Between the parent component and child components, preference is given to child components. For example, if you have a child component like this:

// src/Child.js

import React from "react";
import { Helmet } from "react-helmet";
function Child() {
  return (
    <div>
      <Helmet>
        <title>Child Component Rocks!</title>
      </Helmet>
     {"  "}  This time title will be  Child Component Rocks! 
    </div>
  );
}
export default Child;

And you import this child component (i.e., Child.js) to the parent component (i.e., App.js), the title of the document will change according to the child component, but the meta description and theme-color will not be overwritten.

// src/App.js

import React from "react";
import "./App.css";
import { Helmet } from "react-helmet";
import Child from "./Child";
function App() {
  return (
    <div className="App">
      <Helmet>
        <title>React Helmet Tutorial</title>
        <meta name="description" content="Tutorial for React Helmet" />
        <meta name="theme-color" content="#E6E6FA" />
      </Helmet>

      <header className="App-header">
        Title will not be React Helmet Tutorial <Child />
      </header>
    </div>
  );
}
export default App;

Between two child components, the one that is used later will be given preference. For example, if there are two child components — namely <Child1 /> and <Child2 /> — then the component used later in the App.js file will be given preference.

// src/App.js
import React from "react";
import "./App.css";
import { Helmet } from "react-helmet";
import Child1 from "./Child1";
import Child2 from "./Child2";
function App() {
  return (
    <div className="App">
      <Helmet>
        <title>React Helmet Tutorial</title>
        <meta name="description" content="Tutorial for React Helmet" />
        <meta name="theme-color" content="#E6E6FA" />
      </Helmet>
      <header className="App-header">
        Title will not be React Helmet Tutorial 

        <Child1 />
        <Child2 />
      </header>
    </div>
  );
}
export default App;

In the above code, the title will be set according to the <Child2 /> component.

If the order were to be reversed, it would be set according to <Child1 />:

<header className="App-header">
        Title will not be React Helmet Tutorial 
        <Child2 />
        <Child1 />
</header>

SSR and React Helmet

As discussed above, React Helmet is the perfect partner for server-side-rendered React apps. You can refer here to set up a basic React SSR app.

In your server-side code, right after you call ReactDOMServer’s renderToString or renderToStaticMarkup, you need to use Helmet’s renderStatic method.

Here’s an example for server-side rendering:

// server/index.js
import React from "react";
import { renderToString } from "react-dom/server";
import express from "express";
import App from "./src/App";
import { Helmet } from "react-helmet";
const app = express();  

app.get("/*", (req, res) => {
  const app = renderToString(<App />);
  const helmet = Helmet.renderStatic();

  const html = `
  <!DOCTYPE html>
  <html ${helmet.htmlAttributes.toString()}>
    <head>
      ${helmet.title.toString()}
      ${helmet.meta.toString()}
      ${helmet.link.toString()}
    </head>
    <body ${helmet.bodyAttributes.toString()}>
      <div id="root">
        ${app}
      </div>
      </body>
  </html>
`;
  res.send(html);
});
app.listen(8000);

Each helmet property contains a toString() method, which is used inside the html string. Helmet’s renderStatic returns an instance with all the properties, such as title, meta, link, script, etc., and all of these properties have a toString() method.

react-helmet-async

React Helmet might not always be the best option for SSR since it works synchronously. If you are making asynchronous requests, especially streaming, React Helmet may lead to potential errors and issues.

react-helmet-async is a fork of React Helmet that solves this problem explicitly. You can install it by running the following command:

npm i react-helmet-async

Everything remains the same, except that in react-helmet-async, you need to use HelmetProvider to encapsulate the React tree on both the client and the server.

// src/App.js
import React from "react";
import "./App.css";
import  {Helmet, HelmetProvider } from 'react-helmet-async';
function App() {
  return (
    <HelmetProvider>
    <div className="App">
      <Helmet>
        <title>React Helmet Tutorial</title>
        <meta name="description" content="Tutorial for React Helmet" />
        <meta name="theme-color" content="#E6E6FA" />
      </Helmet>
      <header className="App-header">
        Title will be React Helmet Tutorial 
      </header>
    </div>
    </HelmetProvider>
  );
}
export default App;

Using React Helmet with React Router

React Helmet is especially beneficial when your app uses something like React Router for routing. In such cases, you will need to use React Helmet in every route. Here is an example to illustrate:

// src/App.js
import React from "react";
import { Helmet } from "react-helmet";
import { BrowserRouter, Switch, Route, Link } from "react-router-dom";
import Home from './Home'
import About from './About'
export default function App() {
  return (
    <div>
      <BrowserRouter>
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
        </Switch>
      </BrowserRouter>
    </div>
  );
}

Where Home.js is:

// src/Home.js
import React from "react";
import { Helmet } from "react-helmet";
import {  Link } from "react-router-dom";

const Home = () => (
  <div>
    <Helmet>
      <title>Home</title>
    </Helmet>
    <h2>Home.</h2>
    <Link to="/about">About</Link>
  </div>
);

export default Home

And About.js is:

// src/About.js
import React from "react";
import { Helmet } from "react-helmet";
import {  Link } from "react-router-dom";

const About = () => (
  <div>
    <Helmet>
      <title>About</title>
    </Helmet>
    <h2>About.</h2>
    <Link to="/">Home</Link>
  </div>
);
export default About

The same can be repeated for any number of routes. You can explore this example here.

Conclusion

In this article, we saw how to install and use React Helmet, a document head manager. It is small and easy to use and can be helpful for your projects.

Here are a few resources that you may find useful:

 

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

Which of these topics are you most interested in?
ReactVueAngularNew frameworks
Do you spend a lot of time reproducing errors in your apps?
YesNo
Which, if any, do you think would help you reproduce errors more effectively?
A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Ashutosh Singh Ashutosh is a full-time engineering student, JavaScript developer, and freelance writer. He writes about the fundamentals of JavaScript, Node.js, React, and how to build portfolio projects in JavaScript and React.

Leave a Reply