This is going to be a little long, please forgive me. I’m still a bit of a noob and sometimes things don’t seem to click.

I’ve been trying to get SSR working for a while now with no success. I’ve tried to follow some online tuts and the meteor SSR documentation, however, I’m not really following what’s happening and as a result, I keep getting lost.

I’d like to be able to render public routes using SSR and have others not worry about it.

Instead of putting up one of my previous failed attempts I’ve put up my working app that doesn’t have SSR, with the hope that someone would be kind enough to show me what I’m supposed to do here.

Meteor, React, Helmet

Path: client/main.html

  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, shrink-to-fit=no">

  <div id="react-target"></div>

Path: imports/ui/layouts/App.jsx

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import React from 'react';
import { Helmet } from 'react-helmet';
import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Index = () => <h1>Home page. index test.</h1>;
const Loading = () => <p>Loading...</p>;

const App = props => {
  const { loading } = props;

  if (loading) {
    return (
        <div className="application">
            <meta charSet="utf-8" />
            <title>My App</title>
            <link rel="canonical" href="" />

          <Navbar />

            {/* Public routes - Load these with SSR for google */}
            <Route path="/" exact component={Index} />

            {/* Admin route */}
            <Route path="/admin/users/create" exact component={CreateUsers} {...props} />

  return <Loading />;

const AppContainer = withTracker(() => {
  const loading = Accounts.loginServicesConfigured();

  return {

export default AppContainer;

First of all, it’s probably a typo but it looks like you’re rendering the app when it’s loading and rendering <Loading /> when it has finished loading.

If you only want to SSR public routes, the easiest way is just to check the route before deciding to server render or not:

import URL from 'url';
import React from "react";
import { renderToString } from "react-dom/server";
import { onPageLoad } from "meteor/server-render";

import publicRoutes from '/imports/routes';
import App from 'imports/somewhere.js'

onPageLoad(sink => {
  const url = URL.parse(sink.request.url);
  // check if it's a public route or not
  if (publicRoutes.includes(url.pathname)) {
    sink.renderIntoElementById("app", renderToString(
      <App location={sink.request.url} />

Of course, there’s better ways to compare a route, but the principle applies

Not sure about the rest, it would help if you do post the not-working SSR code so we can help debug.

Sorry, would this file be under imports/startup/server/index.js?

Yeah, there or anywhere run on the server

Also don’t copy paste that, the renderToString part will likely not work directly because of the Router and stuff.

You’ll also need to hydrate the app on the client side if it was server rendered

Sorry, man. Really appreciate the help but you just lost me. I’ve basically just done what you said not to do and I’m getting the error TypeError: Cannot set property 'emulateTransitionEnd' of undefined.

Can you show me what you mean by ‘You’ll also need to hydrate the app on the client side if it was server rendered’.

Check out the guide where it says “Likewise on the client:”

Also check out react-router’s SSR guide. They give you a BrowserRouter for client and StaticRouter for server.

I haven’t had to do that before so I can’t help a huge amount

1 Like

Thanks mate. I’ll check it out.