react-helmet icon indicating copy to clipboard operation
react-helmet copied to clipboard

Meta tags data from API

Open mishkeTz opened this issue 4 years ago • 5 comments

Hi, I am using razzle for SSR and I have helmet configured in my project.

For pages where I want to set content that never changes it works fine. For example on main landing page I have i.e:

<Helmet>
    <title>This is a title</title>
    <meta property="og:title" content='This is also a title' />
</Helmet>

But in case where I open a page of one post posts/:postId I want to show title and other meta tag values dynamically. The title would be the title of that post etc. but I have to wait for API response and it takes some time to fetch data and then in page source I never see meta tags with values of that post.

Here's an example of render method in Post component (I also use redux)

render() {
    const { loading, post } = this.props;

    if (loading) return <PageLoaderComponent />;

    return (
        <div>
            <Helmet><title>{post.title}</title></Helmet>
           ...
        </div>
    )
}

Once the post is fetched I see in my browser that title and other meta tags are changed but page source is 'empty' because of the that is rendered <PageLoaderComponent /> while the post is loading. And when I share that link to facebook or somewhere else, it doesn't pick up the meta tags, basically it's empty, there's nothing in page source.

mishkeTz avatar Sep 10 '19 09:09 mishkeTz

Did you find any solution for the same? I am facing the same issue.

avinash-tiwari avatar Feb 16 '20 10:02 avinash-tiwari

@avinash-tiwari Nah, not really. You need SSR to get different meta tags and static data on each 'page', and that's ok. Bit when it comes to dynamic data in meta tags, like in this case, where I need the data from the API first and then populate meta tags I faced an issue. I couldn't make it work and at the end I gave up.

mishkeTz avatar Feb 16 '20 16:02 mishkeTz

@mishkeTz , I am able to solve the same issue using next.js getInitialProps method. You can also take a look at this. Now I am thinking of moving to Next.js for all my future react projects.

avinash-tiwari avatar Feb 17 '20 09:02 avinash-tiwari

Same issue. Have recently moved a SPA to SSR with Razzle, and would prefer not to move the application to Next.js.

rkimmi avatar May 10 '21 02:05 rkimmi

This is working code from my project. This is a block from the server.js file.

// for all routes
server.get('/*', (req, res) => {
    let cookies;
    if(req.cookies) { cookies = new Cookies(req.headers.cookie); }    // prepare cookie from request for server use

    const context = {};
    const markup = renderToString(
      <CookiesProvider cookies={cookies}>
        <Provider store={store}>
          <StaticRouter context={context} location={req.url}>
            <App />
          </StaticRouter>
        </Provider>
      </CookiesProvider>
    );

    if (context.url) {
      res.redirect(context.url);
    } else {
      const filePath = path.resolve(publicFolder, 'markup.html');
      fs.readFile(filePath, 'utf8', function (err, template) {
        if (err) { return console.log(err); }

        // build response once meta-data is ready
        meta_information(req).then((meta)=> {
          const values = {
            meta_tags: meta,
            css_build: css_assets(),
            js_build: js_assets(),
            markup_html: markup
          };

          const html = mustache.render(template, values);
          res.status(200).send(html);   // return the updated data
        });
      });
    }
  });

The markup.html contains dynamic placeholders, and are built based upon the meta tags from meta_information(req) request. If the promise is fulfilled then html is built by injecting the dynamic values using mustache library (can use any other library) and then return the response within the promise success code block.

anz000 avatar May 10 '21 14:05 anz000