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.
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.
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.
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>
.
// 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>
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>
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 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;
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.
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:
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowJavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.