Search code examples
javascriptreactjsmeta-tagsreact-helmet

How do I generate dynamic meta description and image in React using React Helmet?


My program (https://rohanweb.netlify.app) is sanity API driven. It has blog post so whenever I share particular blog(using react-share), It displays default meta description and image which is inside index.html file. I have used React Helmet for dynamic meta tags but is doesn't seem to be working. No matter what I share, It is displaying default meta and when I remove that from index.html file it displays nothing. I have checked from https://heymeta.com . Here is sample of code for :

<Helmet>
<title>{singlePost.blogTitle}</title>
<link rel="canonical" href={window.location.href} />
<meta property="og:url" content={window.location.href} />
<meta property="og:type" content="website" />

<meta name="twitter:card" content={singlePost.publicImg.asset.url} />

//for Title
<meta property="og:title" content={singlePost.blogTitle} />
<meta itemprop="name" content={singlePost.blogTitle} />
<meta name="twitter:title" content={singlePost.blogTitle}/>

//for Description
<meta property="og:description" content={singlePost.blogDescription} />
<meta itemprop="description" content={singlePost.blogDescription} />
<meta name="twitter:description" content={singlePost.blogDescription} />

//for Image
<meta property="og:image" content={singlePost.publicImg.asset.url} />
<meta itemprop="image" content={singlePost.publicImg.asset.url} />
<meta name="twitter:image" content={singlePost.publicImg.asset.url} />
</Helmet>

Image Demo


Solution

  • React-helmet is not ideal for the solution required. Below is the order in which the browser handles it

    1. Request a page with its path.
    2. Fetch the resources for that page.
    3. Load the default HTML (with meta tags...)
    4. Execute JavaScript codes thereby the meta tags are updated via react-helmet.

    Having your application hosted on a server, the default meta-tags are rendered as fetched. It changes only when a browser requests for a resources on a page, after which the react-helmet functions are executed.

    A Better solution that I could think of would be to configure meta-tags from the server and Hydrate at the front-end.

    Example Below,

    <html>
      <head>
        __PLACEHOLDER_FOR_DYNAMIC_META_TAG__
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
      </body>
    </html>
    

    Read the index synchronously and replace the placeholder with meta-tag needed

    const path = require("path")
    const express = require("express")
    const app = express()
    const fs = require("fs")
    
    const indexFilePath= path.join(__dirname, "build/index.html")
    app.get("/", (req, res) => {
      const myFile = fs.readFileSync(indexFilePath)
      const myMetaTag= "Meta tag details as per requirement"
      const toHydrate = myFile.replace("__PLACEHOLDER_FOR_DYNAMIC_META_TAG__", myMetaTag)
      res.send(toHydrate)
    });
    
    app.get("*", (req, res) =>
      res.sendFile(path.join(__dirname, "build/index.html"))
    )
    const port = process.env.PORT || 5000
    app.listen(port, () => {
      console.log(`Server listening on port ${port}`)
    });
    

    In your react app instead of ReactDOM.render(<App/>, ...) use ReactDOM.hydrate(<App />, ...)

    Or simply go for Next.js :)

    Updated answer for your comment below,

    document.getElementsByTagName('meta')["keywords"].content = "Hello";
    document.getElementsByTagName('meta')["description"].content = "description";