4
votes

Helmet right now is returning empty strings on server. I am not sure if the documentation explains it really well and the existing issues are rarely of any help !

Issue -

   `console.log('title', helmet.title.toString());
    console.log('meta', helmet.meta.toString());
    console.log('link', helmet.link.toString());`

are all empty except title which logs <title data-react-helmet="true"></title>

A very similar problem exists here - react-helmet outputting empty strings on server-side

But it has ejected the app already for SSR. I haven't done so far and would wish not to only for react-helmet. Second, I need to dynamically call the meta tags from API and fill the same.

I am very unclear how these are working. How these pick up meta and links automatically underneath? Don't I need to set them?

I will eventually want to add custom meta tags for every route. But lets' get started for home page atleast for helmet

My implementation as per docs below:

import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import store from '../../src/store';
import {createMemoryHistory } from 'history';
import { ServerStyleSheet } from 'styled-components';
import Loadable from 'react-loadable';
import manifest from '../../build/asset-manifest.json';
import Helmet from 'react-helmet';
import App from '../../src/containers/app';
const path = require('path');
const fs = require('fs');
const history = createMemoryHistory({
  initialEntries: ['/', '/next', '/last'],
  initialIndex: 0
});
const modules = [];
const extractAssets = (assets, chunks) => Object.keys(assets)
    .filter(asset => chunks.indexOf(asset.replace('.js', '')) > -1)
    .map(k => assets[k]);

export default (req, res, next) => {
    const filePath = path.resolve(__dirname, '..', '..', 'build', 'index.html');
    fs.readFile(filePath, 'utf8', (err, htmlData) => {
        if (err) {
            console.error('err', err);
            return res.status(404).end();
        }
        const sheet = new ServerStyleSheet();
        const body = ReactDOMServer.renderToString(
            sheet.collectStyles(<Provider store={store}>
              <ConnectedRouter history={history}>
                  <Loadable.Capture report={m => modules.push(m)}>
                       <App />
                  </Loadable.Capture>
              </ConnectedRouter>
            </Provider>)
        );
        const helmet = Helmet.renderStatic();
        console.log('title', helmet.title.toString());
        console.log('meta', helmet.meta.toString());
        console.log('link', helmet.link.toString());
        const styleTags = sheet.getStyleTags();
        const extraChunks = extractAssets(manifest, modules)
        .map(c => `<script type="text/javascript" src="/${c}"></script>`);
            return res.send(
                htmlData.replace(
                    '<html>',
                    `<html ${helmet.htmlAttributes.toString()}>`
                ).replace(
                    '<head>',
                    `<head>
                        ${helmet.title.toString()}
                        ${helmet.meta.toString()}
                        ${helmet.link.toString()}
                    </head>`
                ).replace(
                        '</head>',
                        `${styleTags}</head>`
                    ).replace(
                    '<div id="root"></div>',
                    `<div id="root">${body}</div>`
                ).replace(
                    '<body>',
                    `<body ${helmet.bodyAttributes.toString()}>`
                ).replace(
                    '</body>',
                    extraChunks.join('') + '</body>'
                )
            );
        });
};
1
Did you come up with a solution for this? I'm facing the same issue that it works fine on the client but not on the server. - geoboy

1 Answers

1
votes

The code here looks good. Helmet.renderStatic() after rendering should give you everything you added while rendering.

Is it working on the client side? If you look at the DOM in something like Chrome Dev tools do you see the title, meta-data tags? If not there is something wrong with the code in your component setting the data.

If it works client side it gets trickier and it could be that the Helmet that is loaded in your component is different than the one you import in your SSR code. This can happen if you bundle your application code with webpack for example, but that doesn't seem to be the case here.