Create-react-app: How to add dynamic meta data using React Helmet

Created on 13 Dec 2016  路  33Comments  路  Source: facebook/create-react-app

I installed https://github.com/nfl/react-helmet

but I don't understand where to add in my src files. Is it something possible?

H

Most helpful comment

I figured it out after tinkering for a while; wrote a post about how to do it:
https://www.kapwing.com/blog/how-to-add-dynamic-meta-tags-server-side-with-create-react-app/

And a demo repo here:
https://github.com/justswim/cra-metatag-demo

Hope this is helpful for folks!

All 33 comments

Can you describe your specific use case in more detail?
What kind of metadata do you want to add and how dynamic is it?

Here the meta data that I want to add:

<Helmet
title={post.title}
meta={[
{ name: 'author', content: "Fishii.shop" },

{ name: 'twitter:site', content: "Fishii.shop" },
{ name: 'twitter:creator', content: "Fishii.shop" },
{ name: 'twitter:title', content: post.title },
{ name: 'twitter:image', content: post.image },

{ property: 'og:title', content: post.title },
{ property: 'og:site_name', content: "Fishii.shop" },
{ property: 'og:type', content: "website" },
{ property: 'og:url', content: "http://fishii.shop/post/"+post.puid },
{ property: 'og:description', content: post.title },
{ property: 'og:image', content: post.image },
{ property: 'og:site_name', content: "Fishii.shop" },

{ name: 'viewport', content: 'width=device-width, maximum-scale=1' },
{ name: 'apple-itunes-app', content: 'app-id=1125423676' },
]}
/>

This react app is hosted on Firebase Hosting.

Is there any particular reason you are not doing this right in the HTML?

I need to have dynamic meta tag like the post title

@jsappme did you experience a specific error while adding react-helmet? It works fine for me in one of my projects.

I need to have dynamic meta tag like the post title

Create React App produces a static bundle with HTML, JS, and CSS. It can鈥檛 possibly give you a dynamic <meta> tag because the result HTML is created ahead of time.

While changing document.title with something like React Helmet makes sense, changing <meta> tags doesn鈥檛 make sense unless your app is server rendered. Server rendering is not a supported feature of Create React App so if you want to use it, you might want to check out some alternatives such as Next.js.

That said, if you don鈥檛 want full server rendering and only need to change <meta> tags, you could do this by hand as described here.

I hope this helps!

existentialism: is it possible to use react-helmet for not a server rendering react app?

@jsappme yep, here's a very trivial example:

// ./src/App.js
import React, { Component } from 'react';
import Helmet from 'react-helmet';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Helmet title="My Title - Test!" />
      </div>
    );
  }
}

export default App;

If you aren't having any actual create-react-app-related issues, you may want to consult react-helmet's docs or raise an issue with them.

I opened an issue on react-helmet too.

here is the code that I tried on my site: https://fishii.shop

import Helmet from "react-helmet"

injectTapEventPlugin();

const App = ({ children }) => (
  <MuiThemeProvider>
  <div className="App">
    <Helmet title="My Title - Test" />

But i still do not see the new title. Are you using server side rendering?

Actually this code does change the title but not the source code of the page, this might be due to the non server rendering.

@jsappme

Yes, it won't change the source because the source is in the static HTML file.
You can find it in public/index.html and edit it to your liking.

Indeed there is no way to make it dynamic because there is no server rendering.
But you can use a trick I linked to above if you use a server like Express in production.

@gaearon I'm trying to generate dynamic meta tags as documented in the link you mentioned.

This part isn't clear to me: "Then, on the server, regardless of the backend you use, you can read index.html into memory and replace OG_TITLE, OG_DESCRIPTION, and any other placeholders with values depending on the current URL."

How would you do that on a simple express server? Here's what I'm working with:

/** 
 *  Create and configure express app 
 */

var app = express();
app.set('port', (process.env.PORT || 3001));
// bodyParser parses application/json data from a POST request
app.use(bodyParser.json()); 
// Set up logger
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));


/**
 * Serve the React app (specifically the build directory)
 * from the node server, using express.static
 */

if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.resolve(__dirname, '../client/build')));
}

/**
 *  Set up routes for our API 
 *  which will handle all inbound requests to the server
 */
routes(app);

/**
 *  Start all our cron jobs
 */
scheduler.start();


/**
 * Run our reactors
 * These are functions which listen for changes in the DB and execute
 * (e.g., When event is added, send host an email)
 */
reactors();

/**
 *  For any requests that were not handled by the API, 
 *  return the React app (which will handle routing on the client)
 */

app.get('*', (request, response) => {
  response.sendFile(path.resolve(__dirname, '../client/build', 'index.html'));
});


/**
 *  Start the express app
 */

app.listen(app.get('port'), () => {
  console.log(`Listening on port ${app.get('port')}`);
});

Thanks

Instead of using sendFile, you would load this file into memory outside the request with fs.readFileSync, and then do the interpolation and send the response in your get('*' handler.

@gaearon Thanks!

I followed the instructions here, but for some reason,

app.get('*', (request, response) => {
  response.sendFile(path.resolve(__dirname, '../client/build', 'index.html'));
});

never gets called. Express seems to serve index.html automatically in production since it has it as a static file. How exactly should I replace the __OG_TITLE__ and __OG_DESCRIPTION__ tags if the '*' route never gets called? Or am I missing something here?

Thanks for your help.

@gaearon @ecastano could you please share a working example of : 'Instead of using sendFile, you would load this file into memory outside the request with fs.readFileSync, and then do the interpolation and send the response in your get('*' handler.' ?

Thank you!

Yeah, a working example would be very appreciated!

Has anyone gotten this to work? Just getting these metatags functioning would be a life-saver :)

I figured it out after tinkering for a while; wrote a post about how to do it:
https://www.kapwing.com/blog/how-to-add-dynamic-meta-tags-server-side-with-create-react-app/

And a demo repo here:
https://github.com/justswim/cra-metatag-demo

Hope this is helpful for folks!

@justswim It works great, but only on first load.
After page reload browser displays placeholders, because SW shows cached index.html version.

@andrOmelianenko What do you mean by SW?

The main point of the metatags is for crawlers like FB or Twitter, so as long as it works on the first load it should be fine. For after your app loads, you can use React Helmet or something to make the tags show up properly on the client side.

@justswim I'm about built-in service worker.

@gaearon would this npm library solve the problem at hand, or will the same problem persist?

https://www.npmjs.com/package/react-meta-tags

@henrybrigham It works in the same way like react-helmet. So it's useless for fb/twitter crawlers till you don't use ssr.
Take a look at the @justswim solution instead.

@justswim I read your blog(https://www.kapwing.com/blog/how-to-add-dynamic-meta-tags-server-side-with-create-react-app/) and gave it a try, that is really helpful! But I also have anther issue that in the main project I am working on, where the client side and server side are separated.

So basically when I want to read client side's index.html file at the server side, it cannot get the file's path name like this at the server side: const filePath = path.join(__dirname, './docs/index.html'); since they live in two different repos.

In this case, is there anyway I can still use your method to dynamically change the meta tag for the webpage? Thank you very much!

@keyanzhang i think you just need to serve your front end app using a server and the create routes with the pages you want a dynamic meta tags on. I have done this and it works.
1.I have my react app and i am serving it with node js express.

  1. I have my backend app separate with some apis to serve my app and i use axios for my calls.

Hi @justswim,
I implemented your technique (https://www.kapwing.com/blog/how-to-add-dynamic-meta-tags-server-side-with-create-react-app/) and OG tags are loading fine in localhost(when I "run node server.js" in terminal).
The production build is deployed on an Azure web server, and I don't know what to tell dev-ops team to run (maybe run "node server.js" or anything else) so that it works fine on Production server as well.
Can anyone suggest or help me as I am not aware of Azure server.

How to add twitter card meta tag using react-helmet to render dynamic data?

In React Helmet, if you want to target and dynamically update server rendered meta tags, add data-react-helmet="true" to the element when you first create it on the server. Otherwise it will just add a new element to the bottom of the head.

I am facing some Kind of issues Using react-helmet. On the beginning I added few libraries then it works fine, after few steps I need to add og meta tags dynamically of FB, then it makes issues for me. I need to append these meta tags with previously added libraries, I am not getting how to figure it out. It makes issues if somebody needs to add something on multiple times.

@amit0shakya Hi, I think the right way to do is to make your html page server side rendered. I met the same problem this April, and finally found a way out following this blog: https://medium.com/styled-components/the-simple-guide-to-server-side-rendering-react-with-styled-components-d31c6b2b8fbf. The way I did is to make this a server side rendering service, handling the requests of a specific route, and deployed it. By this way the meta tags can be dynamically rendered with the data passed back from API calls, and the facebook sharing with customized preview works fine for our app now.

@KeyeZhang I am using Nextjs + Express server for React based project. and still having this issue. And React Helmet issue is I need to add things in head object in html that's why it is showing the issue. on init I am adding few js files after few steps I need to add fb og meta tags dynamically and it removes previously added tags and didn't add og meta tags as well.

I figured it out after tinkering for a while; wrote a post about how to do it:
https://www.kapwing.com/blog/how-to-add-dynamic-meta-tags-server-side-with-create-react-app/

And a demo repo here:
https://github.com/justswim/cra-metatag-demo

Hope this is helpful for folks!

i explain your example

my app router set domain/post:id
in nodejs server side i get id for dynamic meta
resApp.params[0]

and call 1 api for get meta data using request module
full code

app.get('/post/*', function(resApp, response) {

    const filePath = path.resolve(__dirname, './build', 'index.html')
    fs.readFile(filePath, 'utf8', function (err,data) {
      if (err) {
        return console.log(err);
      }
      var url = `*********/guest/Meta/post/${resApp.params[0]}`;

      request(url, { json: true }, (err, res, body) => {
        if (err) { return console.log(err); }

        data = data.replace(/\$OG_TITLE/g, body.title);
        data = data.replace(/\$OG_DESCRIPTION/g, body.description);
        result = data.replace(/\$OG_IMAGE/g, body.thumbnail);
        response.send(result);
      });

    });
  });

hope for helpful

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rdamian3 picture rdamian3  路  3Comments

jnachtigall picture jnachtigall  路  3Comments

fson picture fson  路  3Comments

wereHamster picture wereHamster  路  3Comments

alleroux picture alleroux  路  3Comments