react-helmet
react-helmet copied to clipboard
Meta tags data from API
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.
Did you find any solution for the same? I am facing the same issue.
@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 , 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.
Same issue. Have recently moved a SPA to SSR with Razzle, and would prefer not to move the application to Next.js.
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.