3
votes

I'm using Razzle for using React and Server Side Rendering with React Helmet. I have this problem when you use React Helmet to set meta tags with dynamic values, it's not displayed correctly. But it works if you set the meta tags with static values.

Please take a look at some codes.

SEO.js Component

import React, { Component } from 'react';
import { Helmet } from "react-helmet-async";

class SEO extends Component {
    constructor(props) {
        super(props);
        this.state = {
            title: this.props.title,
            description: this.props.description,
            image: this.props.image
        }
    }

    shouldComponentUpdate(nextProps) {
        if(this.props != nextProps) {
            this.setState({
                title: nextProps.title,
                description: this.props.description,
                image: nextProps.image
            })
            return true;
        } else {
            return false;
        }
    }

    render() { 
        return (
            <div>
                <Helmet>
                    <title>{this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}</title>
                    <meta name="title" content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"} />
                    <meta
                    name="description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    property="og:title"
                    content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
                    />
                    <meta
                    property="og:description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    property="og:image"
                    content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
                    />
                    <meta property="og:url" content="https://volunteerhub.id" />
                    <meta
                    name="twitter:title"
                    content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
                    />
                    <meta
                    name="twitter:description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    name="twitter:image"
                    content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
                    />
                    <meta name="twitter:card" content="summary_large_image" />
                </Helmet>
            </div>
        );
    }
}

export default SEO;

Here is the example of setting up the static meta tags:

import React, {Component} from "react";
import SEO from "../../components/SEO";

class ScheduleContainer extends Component {
    constructor(props) { super(props); }
    render() {
        return(
            <div>
                <SEO 
                    title="Cek Jadwal | Volunteer Hub by Indorelawan"
                    description="Cek jadwal kegiatan di Volunteer Hub! Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia." />
            </div>);
    }
}

And here is the example of setting up the dynamic meta tags:

import React, {Component} from "react";
import axios from "axios";
import SEO from "../../components/SEO";

class EventContainer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            event: {}
        }
    }

    componentDidMount() {
        axios.get('API_URL')
        .then(response => {
            this.setState({ event: response.data.result })
        });
    }

    render() {
        return(
            <div>
                <SEO 
                    title={this.state.event.title}
                    description={this.state.event.description} />
            </div>);
    }
}

Server.js

import RootContainer from "./containers/RootContainer";
import React from "react";
import { StaticRouter } from "react-router-dom";
import express from "express";
import { renderToString } from "react-dom/server";
import { Helmet, HelmetProvider } from "react-helmet-async";

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

const server = express();
server
  .disable("x-powered-by")
  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))
  .get("/*", (req, res) => {
    const context = {};
    const helmetContext = {};
    const markup = renderToString(
      <HelmetProvider context={helmetContext}>
        <StaticRouter context={context} location={req.url}>
          <RootContainer />
        </StaticRouter>
      </HelmetProvider>
    );

    const { helmet } = helmetContext;

    if (context.url) {
      res.redirect(context.url);
    } else {
      res.status(200).send(
        `<!doctype html>
        <html lang="">
        <head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <meta http-equiv="X-UA-Compatible" content="ie=edge" />
            <meta
            name="keywords"
            content="volunteer, hub, by, indorelawan, volunteer hub, volunteer hub by indorelawan, kolaborasi, dimulai, dari, sini, ubah, niat, baik, jadi, aksi, baik, hari, ini"
            />
            <meta name="robots" content="index, follow" />
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <meta name="language" content="Indonesia" />
            <meta name="author" content="Indorelawan" />
            <meta name="msapplication-TileColor" content="#ffffff">
            <meta name="theme-color" content="#222222" />
            ${helmet.title.toString()}
            ${helmet.meta.toString()}
            ${
        assets.client.css
          ? `<link rel="stylesheet" href="${assets.client.css}">`
          : ""
        }
            ${
        process.env.NODE_ENV === "production"
          ? `<script src="${assets.client.js}" defer></script>`
          : `<script src="${
          assets.client.js
          }" defer crossorigin></script>`
        }
        ...
        </head>
        <body>
            <div id="root">${markup}</div>
            <script>
            if ("serviceWorker" in navigator) {
              if (navigator.serviceWorker.controller) {
                console.log("[PWA Builder] active service worker found, no need to register");
              } else {
                // Register the service worker
                navigator.serviceWorker
                  .register("pwabuilder-sw.js", {
                    scope: "./"
                  })
                  .then(function (reg) {
                    console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
                  });
              }
            }
            </script>
        </body>
    </html>`
      );
    }
  });

export default server;

Now that you have seen the code, here is the result when I copy pasted to Google SERP Simulator and WhatsApp:

Static meta tags result from Schedule Page:

Google SERP Simulator Static meta tags - Google SERP Simulator WhatsApp Static meta tags - WhatsApp

Dynamic meta tags result from Event Page:

Google SERP Simulator Dynamic meta tags - Google SERP Simulator WhatsApp Dynamic meta tags - Whatsapp

From the result, it always return the default title and description tag not the title and description passed from the axios. Is is normal behavior or am I doing something wrong?

2
Hey, did you find out a solution for this?G_S
@G_S Unfortunately no. I ended up using NextJS instead. Maybe you should tooDenny Pradipta
I used a razzle and helmet before and it worked fine. Give me some time to catch up on how I did that!Afsanefda
isn't this because of your fetch api? I guess its because of your request!Afsanefda

2 Answers

1
votes

Razzle IS server side rendering, the problem with your dynamic meta tags scenario is that you're relying on data that is fetched in componentDidMount, and componentDidMount, as a lifecycle method from the commit phase, is not called on the server since there's no actual mounting on server side.

NextJS solves this problem for you because of getInitialProps, which is invoked on server and client.

0
votes

Turns out Razzle is not Server Side Rendering. Either you have to define the SEO tags using a custom express server, or just use SSR for React.

I am using NextJS for this and this is a no problem.