I want to implement react-helmet on Meteor. The client side works fine and I can see the tags on the Inspect Element - but how to render it on server side for SEO? I do not understand the documentation.
ReactDOMServer.renderToString(<Handler />);
const helmet = Helmet.renderStatic();
My Code is as follows
index.html (client)
<head>
<body>
<div id="target" />
</body>
</head>
main.js (client)
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from '../imports/startup/client/routes.jsx'
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('target'));
});
I’m using React-Router to render the element to the “target”
main.js (server)
How to get the tags from the server.
import { onPageLoad } from 'meteor/server-render';
Meteor.startup(() => {
onPageLoad((sink) => {
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
});
});
On the about code helmet.meta.toString()
returns empty. I know we need to pass something to let the helmet know the meta tags. But how to do that
Can someone help me understand what I need to write on the server to get this working? Everything except that is working fine.
1 Like
minhna
January 9, 2019, 9:03am
2
In a react component render, if you return something like this.
<Helmet>
<meta name="description" content="some description" />
</Helmet>
then helmet.meta.toString()
will return meta string.
Yes yes. Everything works on the client side. I can see them on the Inspect Element as well but not on the view:source. Please see the code on the main.js (server) I’m not doing something correct for sure.
the server code is missing the server side rendering part of the app:
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
// ...
onPageLoad((sink) => {
const context = {};
const app = ReactDOMServer.renderToString(
<StaticRouter location={sink.request.url} context={context}>
<App />
</StaticRouter>
);
sink.renderIntoElementById('react-root', app);
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
});
the rendering of the <App />
is needed, because Helmet has to know, which components you render with which props.
you will also get the full SSR experience with that
2 Likes
Thanks so much!.. But can I ask what this <App/>
component will be since I have a react-router setup in the client.
This is my code on the CLIENT SIDE
main.js (client) : See I use react-router here…
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from '../imports/startup/client/routes.jsx'
import {Helmet} from 'react-helmet';
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('target'));
});
So what exactly is the <App/>
component on the server side…
what’s in your renderRoutes?
1 Like
export const renderRoutes = () => (
<Router >
<div>
<Switch>
{/* <Route path="/lewqe" component={LoginPage}/> */}
<Route path="/cvweqcm/int-wqelogin" component={LoginPageInternal}/>
<Route exact path="/qwe/hrwe" component={LoginPage}/>
........
All these switch statements
ok, then that is your <App />
you can therefore do:
<StaticRouter location={sink.request.url} context={context}>
{renderRoutes()}
</StaticRouter>
the only change you have to do is, that you have to remove <Router>
from renderRoutes and move it to the client-side code:
Meteor.startup(() => {
render(<Router>{renderRoutes()}</Router>, document.getElementById('target'));
});
I’m still having an error…
When I’m importing the import { renderRoutes } from '../imports/startup/client/routes.jsx'
on the server it is giving me a wierd css error in one of the modules and it doesn’t happen when I remove the import.
node_modules/rodal/lib/rodal.css:2
W20190109-19:03:30.458(5.5)? (STDERR) .rodal,
W20190109-19:03:30.458(5.5)? (STDERR) ^
W20190109-19:03:30.459(5.5)? (STDERR)
W20190109-19:03:30.459(5.5)? (STDERR) SyntaxError: Unexpected token .
I really need help with this… <3
can you help me over TeamViewer or something… That would make my day…
I guess, you have imported css with import "path/to/file.css"
unfortunatly, that does not work with meteor at the moment, see this issue: https://github.com/meteor/meteor/issues/9758 (and please upvote!)
as a workaround, you can soft-link the css file into your project (if not on windows):
ln -s node_modules/rodal/lib/rodal.css .
1 Like
Hey, Thanks for the help! It’s working now…
Just a small tiny problem…
Error running template: Error: Meteor.userId can only be invoked in method calls or publications.
I’ve usedMeteor.userId()
in a couple of places in the client side. Is that a security issue. I even replaced it with this.userId()
and still error. Any suggestions.
how to you retreive your data on the client?
you might need https://github.com/abecks/meteor-fast-render
which provides Meteor.subscribe
on the server, i am not sure if it also fixes Meteor.userId
, but i used in a project and i can invoke Meteor.userId
see this chapter in particular: https://github.com/abecks/meteor-fast-render#data-hydration
and adjust your client and server code accordingly
1 Like
FastRender.onPageLoad(sink => {
}
Says there is no function onPageLoad
Anyways I wrote a function that gets the userId from the server anytime I want. That is fine right ?
minhna
January 10, 2019, 12:00am
16
You should move this file out of client
folder. For example, you can put it in directory /imports/startup/both/routes.jsx
1 Like
this depends on how you do it… can you share it? (its security relevant, so you should make sure, to make it right).
1 Like
also which fastrender package did you install?
1 Like
I installed this package https://github.com/abecks/meteor-fast-render .
Meteor.startup(() => {
FastRender.onPageLoad(sink => {
const context = {};
const app = ReactDOMServer.renderToString(
<StaticRouter location={sink.request.url} context={context}>
{renderRoutes()}
</StaticRouter>
);
sink.renderIntoElementById('target', app);
const helmet = Helmet.renderStatic();
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
});
it says FastRender has no function named onPageLoad.
Also I want to render my body also
sink.appendToHead(app);
The above statement repeats the body twice on my browser. The browser renders the body twice. What am I doing wrong ?