React-router: History and server side rendering integration

Created on 26 Sep 2015  路  6Comments  路  Source: ReactTraining/react-router

I have the following route code,

import React from 'react';
import { createHistory } from 'history';
import {Router, Route} from 'react-router';
import {
    App,
    Chat,
    Home,
    Widgets,
    About,
    Login,
    Track,
    RequireLogin,
    LoginSuccess,
    Survey,
    NotFound,
  } from 'containers';

const history = createHistory();

export default function() {
  return (
    <Router history={history}>
      <Route component={App}>
        <Route path="/" component={Home}/>
        <Route path="/widgets" component={Widgets}/>
        <Route path="/about" component={About}/>
        <Route path="/login" component={Login}/>
        <Route path="/track/:id" component={Track}/>
        <Route component={RequireLogin}>
          <Route path="/chat" component={Chat}/>
          <Route path="/loginSuccess" component={LoginSuccess}/>
        </Route>
        <Route path="/survey" component={Survey}/>
        <Route path="*" component={NotFound}/>
      </Route>
    </Router>
  );
}



and I get this error (it did start when I added createHistory).

[1] [piping] can't execute file: /Users/bortignon/dev/ReactTracklistMe/bin/server.js
[1] [piping] error given was: Error: Invariant Violation: Browser history needs a DOM
[1]     at Object.invariant [as default] (/Users/bortignon/dev/ReactTracklistMe/node_modules/history/node_modules/invariant/invariant.js:44:15)
[1]     at Object.createBrowserHistory [as createHistory] (/Users/bortignon/dev/ReactTracklistMe/node_modules/history/lib/createBrowserHistory.js:39:25)
[1]     at Object.<anonymous> (/Users/bortignon/dev/ReactTracklistMe/src/routes.js:18:17)
[1]     at Module._compile (module.js:460:26)
[1]     at normalLoader (/Users/bortignon/dev/ReactTracklistMe/node_modules/babel-core/lib/api/register/node.js:199:5)
[1]     at Object.require.extensions.(anonymous function) [as .js] (/Users/bortignon/dev/ReactTracklistMe/node_modules/babel-core/lib/api/register/node.js:216:7)
[1]     at Module.load (module.js:355:32)
[1]     at Module._load (module.js:310:12)
[1]     at Function.module._load (/Users/bortignon/dev/ReactTracklistMe/node_modules/piping/lib/launcher.js:32:16)
[1]     at Module.require (module.js:365:17)
[1]     at require (module.js:384:17)

Most helpful comment

I'm having the same problem using history, react 0.14 and react-router 1.0.0-rc3.

I'm following the server-side example from the guides, and have a front-end webpack entry at client.js. In client.js, I either have ReferenceError: document is not defined, or Browser history needs a DOM errors.

Server entry:

module.exports = function( req, res, next ) {
    match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
        if (error) {
            res
                .status(500)
                .send(error.message);
        } else if (redirectLocation) {
            res.redirect(302, redirectLocation.pathname + redirectLocation.search);
        } else if (renderProps) {
            res
                .status( 200 )
                .set( 'Content-Type', 'text/html' )
                .send( '<!doctype html>' +
                    renderToString(
                        [ <RoutingContext {...renderProps} />,
                        <HtmlDocument /> ]
                    )
                );
        } else {
            res
                .status(404)
                .send('Not found');
        }
    })

};

client.js:

import { render } from 'react-dom';
import routes from './routes';

render( routes, document.getElementById('app') )

And my routes.jsx:

import React from 'react';
import { Router, Route, IndexRoute } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

import Application from './Application';
import Index from './pages/index';
import App from './pages/app';
import Auth from './pages/auth';
import Login from './pages/auth/components/Login';
import Signup from './pages/auth/components/Signup';
import NotFound from './pages/notFound';

var routes = (
    <Router history={createBrowserHistory()}>
        <Route path="/" component={Application}>
        <IndexRoute component={Index} />
            <Route path="app" component={App} onEnter={ App.requireAuth } />
            <Route path="auth" component={Auth} />
                <Route path="signup" component={Signup} />
                <Route path="login" component={Login} />
            <Route path="*" component={NotFound} />
        </Route>
    </Router>
);

export default routes;

All 6 comments

@nicolabortignon You cannot use the BrowserHistory on the server. For server-side rendering you have to use createLocation and create a one-off location object which you can use with match in order to figure out what needs to be rendered on the server.

Take a look at https://github.com/rackt/react-router/blob/master/docs/guides/advanced/ServerRendering.md as an example.

Note: For universal application you generally need two entry/bootstrap files - one for the client and one for the server - because albeit them reusing mostly the same application, the bootstrap process is different.

@johanneslumpe thanks for the pointer! I'm still struggling in grasping how everything works, but I'll dig into that blob!

Thanks

@nicolabortignon sure no problem :) If this is solved for now feel free to close, so we know that this is taken care of.

I'm having the same problem using history, react 0.14 and react-router 1.0.0-rc3.

I'm following the server-side example from the guides, and have a front-end webpack entry at client.js. In client.js, I either have ReferenceError: document is not defined, or Browser history needs a DOM errors.

Server entry:

module.exports = function( req, res, next ) {
    match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
        if (error) {
            res
                .status(500)
                .send(error.message);
        } else if (redirectLocation) {
            res.redirect(302, redirectLocation.pathname + redirectLocation.search);
        } else if (renderProps) {
            res
                .status( 200 )
                .set( 'Content-Type', 'text/html' )
                .send( '<!doctype html>' +
                    renderToString(
                        [ <RoutingContext {...renderProps} />,
                        <HtmlDocument /> ]
                    )
                );
        } else {
            res
                .status(404)
                .send('Not found');
        }
    })

};

client.js:

import { render } from 'react-dom';
import routes from './routes';

render( routes, document.getElementById('app') )

And my routes.jsx:

import React from 'react';
import { Router, Route, IndexRoute } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

import Application from './Application';
import Index from './pages/index';
import App from './pages/app';
import Auth from './pages/auth';
import Login from './pages/auth/components/Login';
import Signup from './pages/auth/components/Signup';
import NotFound from './pages/notFound';

var routes = (
    <Router history={createBrowserHistory()}>
        <Route path="/" component={Application}>
        <IndexRoute component={Index} />
            <Route path="app" component={App} onEnter={ App.requireAuth } />
            <Route path="auth" component={Auth} />
                <Route path="signup" component={Signup} />
                <Route path="login" component={Login} />
            <Route path="*" component={NotFound} />
        </Route>
    </Router>
);

export default routes;

@almccann For questions and support, please visit our channel on Reactiflux or Stack Overflow. The issue tracker is exclusively for bug reports and feature requests.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

misterwilliam picture misterwilliam  路  3Comments

hgezim picture hgezim  路  3Comments

ryansobol picture ryansobol  路  3Comments

wzup picture wzup  路  3Comments

davetgreen picture davetgreen  路  3Comments