4
12 Comments

SOLVED: React App with dynamic meta tags without SSR?

Hey all!

Does anyone know if there's a possibility to set truly dynamic meta tags per page in a React App without Server Side Rendering?

In a project I'm currently building, I have single pages for books and authors with a route like /book/{bookId} and would like to set meta tags. From my research something like react-helmet will work for the client but not to have those meta tags read by social media sites and search engine crawlers.

Am I correct in assuming that I will need to use SSR to achieve this?

Thanks in advance for any hint!

  1. 5

    I wrote a blog post about it sometime back, it's not detailed, but I think you will understand.

    EDIT: Updated the blog post and added a tutorial

    How To Add Meta Tags in Client-side React App

    TLDR: serve your index.html using express or something and replace dynamic tags based on a JSON config.

    1. 1

      How to replace the values of meta tags dynamically when you server the HTML file via express server. How to inject the values ?

      1. 1

        Hey, I have updated the blog post, have a look.

    2. 1

      Very cool, thanks, will look into this as an alternative to going full SSR!

  2. 3

    After exploring a few different options, I've now solved this for my use case. Thanks again to @sachinverma and @joshtronic for your comments!

    I first considered rebuilding everything to full SSR with Next.js but figured that would be overblown at this point (want to launch an MVP first).

    My final solution for dynamically inserting meta tags albeit sticking with a client rendered create-react-app setup now looks like this (I currently use the full stack of Firebase, including Hosting and Cloud Functions):

    I add the meta tags to the pages dynamically with React Helmet (https://www.npmjs.com/package/react-helmet), since I'm making a database query to get the right information for dynamic routes, this is way too slow to be picked up by most web crawlers.

    Thus I implemented http://prerender.io/ - this will detect crawlers, cache the appropriate sites and serve the appropriate sites.

    To get this working, I copy the index.html into the Firebase Cloud functions folder on build (package.json: "build": "react-scripts build && cp build/index.html functions/web/index.html").

    Then in the firebase.json config, instead of rewriting routes directly to index.html, I send all requests through a Firebase Cloud Function:
    {
    "source": "**",
    "function": "preRender"
    }

    Finally, my cloud function takes all requests and serves the index.html but uses the prerender.io middleware (that will detect crawlers and serve from cache):

    const fs = require("fs")
    const express = require("express")
    const preRender = express()
    
    preRender.use(
        require("prerender-node").set("prerenderToken", "PRERENDER_TOKEN")
    )
    
    preRender.get("*", (req, res) => {
        res.status(200).send(fs.readFileSync("./web/index.html").toString())
    })
    
    exports.preRender = functions.https.onRequest(preRender)`
    

    The prerender.io pricing, of course, gets expensive if you're serving lots of sites. Until I reach scale, this is no concern for me, in the future, I might just build my own script for caching and serving sites to crawlers using puppeteer (https://developers.google.com/web/tools/puppeteer) and spider-detector (https://www.npmjs.com/package/spider-detector) as outlined i.e. here: https://medium.com/front-end-weekly/building-a-pre-renderer-for-spas-108f956f8b

    I hope this can help some other folks trying to do SEO / link previews with a client rendered React app, I might put this into a better-structured blog post down the road. ✌🏻

    1. 1

      @RBouschery Could I see your full code for this? I'm having trouble putting it all together.

      1. 1

        Hey, sorry, somehow your comment escaped me. Do you still need help on this?

        1. 2

          @RBouschery Actually, I got it working in the end without React Helmet and instead used SSR through Firebase cloud functions. Thank you for checking up.

  3. 2

    Yeah, pretty sure that's still an accurate caveat to SPAs.

    How are you serving your index.html file, coming out of Node+Express/PHP/etc or served directly from say, nginx or apache?

    If you are serving it up statically, could make a small adjustment to serve just the index.html up from a dynamic server and inject what you need in it, applicable ONLY on the first page load (which I presume is how most indexing/bot systems will hit it). Saves from having to setup a whole caching layer for serving up cached pages and all of that stuff.

    1. 2

      Thanks for the quick reply. Right now serving it up from node via Firebase Hosting. I've read a little more and as SEO and good OG meta tags will probably be critical for my project, I'm considering re-writing to use Next.js and host on Zeit. If anyone's interested to learn how that will go, I'll share my progress here ;)

      1. 2

        Always interested in that kind of stuff, post/share away!!

Trending on Indie Hackers
After 10M+ Views, 13k+ Upvotes: The Reddit Strategy That Worked for Me! 39 comments Getting first 908 Paid Signups by Spending $353 ONLY. 20 comments 🔥Roast my one-man design agency website 18 comments Launch on Product Hunt after 5 months of work! 16 comments Started as a Goodreads alternative, now it's taking a life of its own 12 comments I Sold My AI Startup for $1,500 and I'm Really Happy About It 11 comments