Generate dynamic meta-tags 
for your client-side rendered web app

Generate dynamic meta-tags for your client-side rendered web app

Why do we need this?

Let's consider a youtube video example, whenever we share a video on any social media platform say twitter.com, the link also shows some images and text giving more context to what that video is all about. See the below example.

So whenever we want to give some personalized touch to the links shared across social media platforms, we can consider generating dynamic meta-tags.

Let's start

Generally in server-side rendered apps, HTML pages are generated on the server itself and then it is served as a response to the user request. So it is easier to generate dynamic meta-tags in SSR apps.

But in client-side rendered apps, it's mostly a single-page application (SPA). So whenever the user requests for the page, the same HTML is served, i.e. its <head> tag will be static, and only the components will be updated inside the <body> tag.

So what actually the problem is? You see that whenever we request for the page we we get the same HTML, and we might think of updating the <head> tag which has <meta> tags inside it using javascript, but there are lots of social media platform bots who can't execute the javascript, then how can we generate dynamic meta tags which will generate open graph social metadata?

Solution

  • We will be using firebase function to achieve this assuming you have hosted your app on firebase.

  • Each firebase project has a hosting configuration which is firebase.json file, where we can configure redirects and match patterns for triggering firebase functions.

      {
       hosting: {
            "rewrites": [
              {
                // /my-blog/:blogId
                "regex": "/my-blog/[A-Za-z0-9-]+[^/]$",
                "function": "generateDynamicMetaTags"
              },   
           ]
        }
      }
    

    So with the above configuration what we are actually telling firebase is that whenever there is a request that has a pattern like /my-blog/:blogId
    then trigger the generateDynamicMetaTags function.

  • Whenever we paste the link on the social media platforms the platform bot makes a request to the server, fetches the associated head tag from that link, and uses that data to generate open graph data. So we need to detect if the request is made by a bot or by a genuine user, for that we can use isbot library, which will help to detect the bots.

  • Below is the code for generateDynamicMetaTags function.

import isbot from 'isbot';

export const generateDynamicMetaTags = functions
  .https.onRequest(async (request, response): Promise<void> => {
      // note the "/r" in the URL
      const blogUrl = `https://my-blog/r/:blogId`;
      if (isbot(request.get('user-agent'))) {
          // get the blog id
          const blogId = request.originalUrl.split('/').filter((path) => !!path)?.[1] ?? '';

          // fetch the blog's meta data
          const blogMetadata = await getBlogMetaData(blogId);
          const { title, name, author } = blogMetadata;
          const ogImageUrl =  'your_OG_image_URL'

          const metaTags = `
                <!DOCTYPE html>
                    <html>
                      <head>
                        <title>${title}</title>
                        <meta name="title" content="${title}" />
                        <meta name="description" content="${author}'s blog" />
                        <meta property="og:type" content="website" />

                        <!-- Twitter -->
                        <meta property="twitter:card" content="summary_large_image" />
                        <meta property="twitter:url" content="${blogUrl}" />
                        <meta property="twitter:title" content="${title}" />
                        <meta property="twitter:description" content="${author}'s blog"  />
                        <meta property="twitter:image" content="${ogImageUrl}" />

                        <!-- Open Graph / Facebook -->
                        <meta property="og:type" content="website" />
                        <meta property="og:url" content="${blogUrl}" />
                        <meta property="og:title" content="${title}" />
                        <meta property="og:description" content="${author}'s blog"  />
                        <meta property="og:image" content="${ogImageUrl}" />
                  </head>        
              <body></body>
            </html>`

          response.set('Cache-Control', 'no-cache');
          response.status(200).send(metaTags);
        } else {
          // redirect to the incoming request URL
          response.status(302).redirect(blogUrl);
        }
  });
  • If you notice that when the request is not made by a bot then we are redirecting it to https://my-blog/r/:blogId URL, it contains /r/ in between, but why?

    So if we redirect to the original URL ie https://my-blog/:blogId then it again matches the pattern that we have in firebase hosting config, and it will again trigger the function, infinite loop, so to avoid this we redirected it to a URL that won't match the pattern.

  • Also this change we need to handle it on client-side routing, so for that we need to setup a client-side redirect.

      if (pathname === '/r/:blogId'){
          // If you are using react-router then use `useNavigate` hook
          navigate(window.location.pathname.replace("/r", ""))
      }
    

    And we are done :)

If you learned something new, from this blog then do share it with others too. Also, this is not the only way to generate dynamic meta-tags, so yeah do explore more.

References:

https://www.netlify.com/blog/dynamically-generate-open-graph-image-variants/

https://www.srmullen.com/articles/firebase-social-meta/

Connect with me on Twitter, GitHub, and LinkedIn.