Search code examples
javascriptreactjsreact-routerreact-router-domrollup

react router only displaying main route


I am using react router dom version 5.0.1 for a simple react app, and i used rollup for bundling, here is my router component

      return(
            <Router>
                <Switch>
                <Route path='/'       component={Main} />
                <Route path='/hello'  component={Hello} />
                <Route path='/login'   component={Login} />
                </Switch>
            </Router>
    )

the problem is that its only displaying the main route at localhost:8000/ but when i try to access localhost:8000/hello or localhost:8000/login it gives me this error

    404 Not Found

    C:\Users\omar_\Desktop\form-builder\form-builder\frontend\public\hello

    (rollup-plugin-serve)

here is my rollup.config

    import babel from "rollup-plugin-babel";
    import resolve from 'rollup-plugin-node-resolve';
    import commonjs from 'rollup-plugin-commonjs';
    import replace from 'rollup-plugin-replace';
    import serve from 'rollup-plugin-serve'

    export default {
input: 'src/index.js',
plugins: [

    resolve({
        browser: true,
    }),
    commonjs({
        include: [
            'node_modules/**',
        ],
        exclude: [
            'node_modules/process-es6/**',
        ],
        namedExports: {
            'node_modules/react/index.js': ['Children', 'Component', 'PropTypes', 'createElement'],
            'node_modules/react-dom/index.js': ['render'],
            'node_modules/react-is/index.js': ['isValidElementType'],
        },
    }),
    babel({
        exclude: "node_modules/**",
    }),
    replace({
        'process.env.NODE_ENV': JSON.stringify('development'),
    }),
    serve('public')
],
output: {
    file: "public/bundle.js",
    format: "cjs",
    sourcemap: 'inline'
}

};


Solution

  • Short Answer:

    The issue is that the rollup-plugin-serve server is not configured to handle 404s. That happens with Client Side Routers.

    Two things that will most likely fix the issue:

    Change this

        serve('public')
    

    To this

        serve({ contentBase: 'public', historyApiFallback: true })
    

    If this doesn't fix it, check the index.html in the public folder, as the issue could be that it doesn't refer the index.js and some other resources from root.

    ex:

    <html>
      <head>
        <title>Application</title>
      </head>
      <body>
        <script src="/index.js"></script>
      </body>
    </html>
    

    Please notice the / in /index.js, it's important. If you'll have it relative(<script src="index.js"></script>) it might cause problems with CSR.

    A slightly longer answer:

    Client Side Router(CSRs) work by wrapping the browser location api, you can make special links in the page that it can intercept.

    When you first load the page, by typing the url in the browser, there is no javascript running in the page yet, so the request goes to the server, server must know that this is a frontend page and return the root html, even though there aren't any matching routes on server side.

    The historyApiFallback: true flag in rollup-plugin-serve configures it to return the index.html page from public folder every time it can't match a url.

    This can cause issues when referencing relative resources in index.html. For example:

    • One calls an endpoint /users/1
    • Rollup serve can't find a resource for it so it returns index.html by default
    • index.html references a relative index.js <script src="index.js"></script>
    • The browser tries to get the resource by doing a get for /users/1/index.js
    • Rollup serve can't find the static resource and returns index.html by default