Meteor SSR Attempt number two

I’m trying to get SSR working with Meteor, React and React Router V4. In the example below, I’m not getting any errors, I just get a blank screen. What am I doing wrong here?

Additional information: If I remove the <Router>...</Router> and just put in something like <h1>My App</h1> within the App file, the page displays ‘My App’.

Path: startup/server.js

onPageLoad(sink => {
  const initialState = {
    siteMeta: {
      owner: 'Chris Visser',
      version: '0.3.8',
      publishedAt: new Date('2018-05-10'),
    }
  };

  sink.renderIntoElementById('app', renderToNodeStream(
    <App location={sink.request.url} initialState={initialState}/>
  ));

  // Push EJSON stringified state to the client
  sink.appendToBody(`
    <script id="preloaded-state">
      window.__PRELOADED_STATE__ = ${EJSON.stringify(initialState)} 
    </script>
  `)
});

Path: startup/client.js

// Stringify back the preloaded state into its original EJSON string form. 
// Then use the EJSON parser to parse rich content types
const initialState = EJSON.parse(JSON.stringify(window.__PRELOADED_STATE__));

delete window.__PRELOADED_STATE__; // Remove what we don't need anymore

onPageLoad(async sink => {
  const App = (await import('../ui/App.jsx')).default;
  ReactDOM.hydrate(
    <App initialState={initialState}/>,
    document.getElementById('app')
  );
});

Path: ui/App.jsx

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

export default ({ initialState }) => {
    console.log('initialState', initialState);
    return (
        <Router>
            <Switch>
            <Route path="/" exact component={Index} />
            <Route path="/login" exact component={Login} />
            </Switch>
        </Router>
    )
};

Try to replace renderToNodeStream by renderToString

now I’m getting Error running template: Error: Invariant failed: Browser history needs a DOM on the server.

With ReactRouter, you need to use BrowserRouter on the browser and StaticRouter when server side rendering

1 Like

I use

import { Route, Switch } from 'react-router-dom';

It works on both client and server.

1 Like

What @coagmano means is you should render 2 different React components on the server and client:

On the server, render this to a string:

<StaticRouter>
  <YourApp />
</StaticRouter>

On the client, hydrate the app with this:

<BrowserRouter>
  <YourApp />
</BrowserRouter>
2 Likes

Have you tried a package to make your life easier? communitypackages:react-router-ssr would probably do the trick. Take a look at my demo if you need an implementation example.

I notice in the docs:

storeOptions - An object that contains the options for a redux store.

Does the package require the app to use redux?

That worked. If I’m understanding this correctly, all pages are now SSR. Is there a way to make some SSR and some client side rendered.

No, redux is completely optional.

You can examine the sink.request.path and only run the SSR code for pages you want to render.

1 Like