Server-render and SEO

Hey everyone, just wondering if using the “server-render” package will allow Meteor compete with basic html/wordpress/etc pages when it comes to SEO or are there still some hurtles to overcome for search engines to see Meteor-based websites as intended?

Thanks!
Kris

2 Likes

I am wondering about this too. Also, are there any good tutorials about getting this working with react-router v3 or v4? Or are you better off just using FlowRouter (I’d rather use a more popular non-meteor solution if possible though)

I can show you how we use it in our application:

lib/main.js

import React from 'react';
import {App} from "../imports/ui/app";
import {Helmet} from 'react-helmet';

if(Meteor.isClient) {

    import { render } from 'react-dom';

    render(<App />, document.getElementById('app'));

} else {

    import { renderToString } from "react-dom/server";
    import { onPageLoad } from "meteor/server-render";
    import NodeCache from 'node-cache';

    const renderCache = new NodeCache({stdTTL: 432000}); // 5 days

    onPageLoad(sink => {

        const path = sink.request.url.path;
        let htmlString = renderCache.get(path);
        if(!htmlString) {
            htmlString = renderToString(<App location={path} />);
            const helmet = Helmet.renderStatic(); // Needs to be called after renderToString

            renderCache.set(path,htmlString);
            renderCache.set(path + "_title",helmet.title.toString());
            renderCache.set(path + "_meta",helmet.meta.toString());
            renderCache.set(path + "_link",helmet.link.toString());
        }

        sink.appendToHead(renderCache.get(path + "_title"));
        sink.appendToHead(renderCache.get(path + "_meta"));
        sink.appendToHead(renderCache.get(path + "_link"));
        sink.renderIntoElementById("app", htmlString);
    });

}

And within your routes (React-Router):

import {browserHistory, createMemoryHistory, Router} from 'react-router';

class Routes extends React.Component {

    history = (Meteor.isClient) ? browserHistory : createMemoryHistory(this.props.location);

    render() {

       return (
           <Router history={this.history}>
              [...your routes...]
           </Router>
       );
    }
}
6 Likes

Wow. That looks a lot more straight forward than I was expecting.

If you don’t mind me asking, what’s going on in the appendToHead calls you are doing for _title, _meta, and _link? Are those specific to Helmet (never used it before)? Or are you doing something home made here?

Hey,
yeah, we add the meta/link tags to the output. On the first part, we cache those values too:

renderCache.set(path + "_title",helmet.title.toString());

path + “_title” is the key for the memory cache. At the end we add all generated meta/link tags to our response from the server:

sink.appendToHead(renderCache.get(path + "_title"));

Helmet has a doc for how you can use it with SSR. It’s pretty easy. The only thing I’m missing is how I can manipulate the HTML tag to add the lang attribute.

Hey X
Thanks to @benjamn for ssr. Besides Julian Ćwirko’s work with ssr and meteor which uses redux at julian.io. - your use of @XTA ssr and meteor with https://www.muzica.io/ is exceptional – great work. Is there any chance that you would be prepared share a basic boilerplate or submit your alternative ssr example to the meteor blog. Awesome work and keep it.

1 Like

How are you getting those routes to work? You’re not importing it in main.js.

1 Like