react-helmet Meteor Server Side Rendering react-router

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

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 :wink:

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(() =&gt; {

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 /> :wink:

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…:heartpulse::heart:

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.

:smiley::heart:

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 ?

https://atmospherejs.com/communitypackages/react-router-ssr

1 Like

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 ?

Any help here… …