107

I have created a single page web app using react js. I have used webpack to create bundle of all components. But now I want to create many other pages. Most of pages are API call related. i.e. in the index.html, I have displayed content from API. I want to insert content in another page parsing data from API. Webpack compresses everything of react in a file which is bundle.js. However, the configuration of webpack is as follow:

const webpack = require('webpack');

var config = {
entry: './main.js',

output: {
    path:'./',
    filename: 'dist/bundle.js',
},

devServer: {
    inline: true,
    port: 3000
},

module: {
    loaders: [
        {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel',

            query: {
                presets: ['es2015', 'react']
            }
        }
    ]
},

plugins: [
    new webpack.DefinePlugin({
        'process.env': {
            'NODE_ENV': JSON.stringify('production')
        }
    }),
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        }
    })
]
}

module.exports = config;

Now, I am confused what kind of configuration of webpack will be for other page or what is the correct way to build multi-pages app using react.js

StreetCoder
  • 9,871
  • 9
  • 44
  • 62
  • 1
    you can have multiple output files for each page that you want in your application. – N8FURY Jan 31 '17 at 11:57
  • 1
    Let me include some context here, by default `npm run build` creates a static index.html, the static nature which is important here for making a static read only hosting website, now generally websites have an about page, page1, page2 etc, which can be very large text/image pages, the entire bundle in single index.html hurts loading time and responsiveness of the site on a slow connection or device, so how do we split up the output pages into individual pages to help with responsiveness – devssh Aug 23 '21 at 14:45

6 Answers6

88

Preface

This answer uses the dynamic routing approach embraced in react-router v4+. Other answers may reference the previously-used "static routing" approach that has been abandoned by react-router.

Solution

react-router is a great solution. You create your pages as Components and the router swaps out the pages according to the current URL. In other words, it replaces your original page with your new page dynamically instead of asking the server for a new page.

For web apps I recommend you read these two things first:

Summary of the general approach:

1 - Add react-router-dom to your project:

Yarn

yarn add react-router-dom

or NPM

npm install react-router-dom

2 - Update your index.js file to something like:

import { BrowserRouter } from 'react-router-dom';

ReactDOM.render((
  <BrowserRouter>
    <App /> {/* The various pages will be displayed by the `Main` component. */}
  </BrowserRouter>
  ), document.getElementById('root')
);

3 - Create a Main component that will show your pages according to the current URL:

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

import Home from '../pages/Home';
import Signup from '../pages/Signup';

const Main = () => {
  return (
    <Switch> {/* The Switch decides which component to show based on the current URL.*/}
      <Route exact path='/' component={Home}></Route>
      <Route exact path='/signup' component={Signup}></Route>
    </Switch>
  );
}

export default Main;

4 - Add the Main component inside of the App.js file:

function App() {
  return (
    <div className="App">
      <Navbar />
      <Main />
    </div>
  );
}

5 - Add Links to your pages.

(You must use Link from react-router-dom instead of just a plain old <a> in order for the router to work properly.)

import { Link } from "react-router-dom";
...
<Link to="/signup">
  <button variant="outlined">
    Sign up
  </button>
</Link>
Anirudha Gupta
  • 9,073
  • 9
  • 54
  • 79
Lucas Andrade
  • 4,315
  • 5
  • 29
  • 50
  • 2
    Works great however, Add a line in section no. 1 e.g. import ReactDOM from 'react-dom' Plus use useHistory to move from one screen to another programmatically. – Amit Bravo May 10 '21 at 11:32
  • 2
    Note that from react-router-dom v6 Switch has been replaced by Routes – Callum Jan 15 '22 at 15:19
  • Note that `component={Home}` has been replaced by `element={}` in v6 (step 3). – Daquisu Feb 16 '23 at 20:45
75

(Make sure to install react-router using npm!)

To use react-router, you do the following:

  1. Create a file with routes defined using Route, IndexRoute components

  2. Inject the Router (with 'r'!) component as the top-level component for your app, passing the routes defined in the routes file and a type of history (hashHistory, browserHistory)

  3. Add {this.props.children} to make sure new pages will be rendered there
  4. Use the Link component to change pages

Step 1 routes.js

import React from 'react';
import { Route, IndexRoute } from 'react-router';

/**
 * Import all page components here
 */
import App from './components/App';
import MainPage from './components/MainPage';
import SomePage from './components/SomePage';
import SomeOtherPage from './components/SomeOtherPage';

/**
 * All routes go here.
 * Don't forget to import the components above after adding new route.
 */
export default (
  <Route path="/" component={App}>
    <IndexRoute component={MainPage} />
    <Route path="/some/where" component={SomePage} />
    <Route path="/some/otherpage" component={SomeOtherPage} />
  </Route>
);

Step 2 entry point (where you do your DOM injection)

// You can choose your kind of history here (e.g. browserHistory)
import { Router, hashHistory as history } from 'react-router';
// Your routes.js file
import routes from './routes';

ReactDOM.render(
  <Router routes={routes} history={history} />,
  document.getElementById('your-app')
);

Step 3 The App component (props.children)

In the render for your App component, add {this.props.children}:

render() {
  return (
    <div>
      <header>
        This is my website!
      </header>

      <main>
        {this.props.children}
      </main>

      <footer>
        Your copyright message
      </footer>
    </div>
  );
}

Step 4 Use Link for navigation

Anywhere in your component render function's return JSX value, use the Link component:

import { Link } from 'react-router';
(...)
<Link to="/some/where">Click me</Link>
nbkhope
  • 7,360
  • 4
  • 40
  • 58
  • 28
    I am confused a a beginner here. Isn't react-router for single page apps? I mean, if you have one index.html file then only that is going to be a page? Others will be injected as in single paged apps right? – ICanKindOfCode Dec 26 '17 at 15:45
  • 3
    This is not Multipage app in webpack. If you had more than 1 entry then you have multi page app (https://webpack.js.org/concepts/entry-points/#multi-page-application) – Homayoun Behzadian Jan 30 '19 at 07:25
  • 1
    How could I share state between these different page components? – Robin Métral Feb 07 '19 at 19:00
  • 2
    @robinmetral use something like Redux. Or you could just have a parent component hold the data, then you pass it down to the multiple routes under it (this is a concept called "Container" component). – nbkhope Feb 07 '19 at 21:51
  • Thank you @nbkhope! I'll look up container components. I found [a good series here](https://css-tricks.com/learning-react-container-components/) if anyone's interested :) – Robin Métral Feb 07 '19 at 22:34
  • 2
    `Attempted import error: 'hashHistory' is not exported from 'react-router' (imported as 'history').` – Philip Rego May 24 '20 at 03:13
12

This is a broad question and there are multiple ways you can achieve this. In my experience, I've seen a lot of single page applications having an entry point file such as index.js. This file would be responsible for 'bootstrapping' the application and will be your entry point for webpack.

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Application from './components/Application';

const root = document.getElementById('someElementIdHere');

ReactDOM.render(
  <Application />,
  root,
);

Your <Application /> component would contain the next pieces of your app. You've stated you want different pages and that leads me to believe you're using some sort of routing. That could be included into this component along with any libraries that need to be invoked on application start. react-router, redux, redux-saga, react-devtools come to mind. This way, you'll only need to add a single entry point into your webpack configuration and everything will trickle down in a sense.

When you've setup a router, you'll have options to set a component to a specific matched route. If you had a URL of /about, you should create the route in whatever routing package you're using and create a component of About.js with whatever information you need.

Chris Charabaruk
  • 4,367
  • 2
  • 30
  • 57
Taylor Jones
  • 402
  • 4
  • 10
6

Following on from the answer above by @lucas-andrade: for react-router versions >= 6, Switch no longer exists (see https://github.com/remix-run/react-router/issues/8439). The content of step 3 becomes:

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

import Home from '../pages/Home';
import Signup from '../pages/Signup';

const Main = () => {
  return (
    <Routes>
      <Route path='/' element={Home}></Route>
      <Route path='/signup' element={Signup}></Route>
    </Routes>
  );
}

export default Main;
Erçin Dedeoğlu
  • 4,950
  • 4
  • 49
  • 69
elukem
  • 1,068
  • 10
  • 11
2

The second part of your question is answered well. Here is the answer for the first part: How to output multiple files with webpack:

{
    entry: {
        outputone: './source/fileone.jsx',
        outputtwo: './source/filetwo.jsx'
    },
    output: {
        path: path.resolve(__dirname, './wwwroot/js/dist'),
        filename: '[name].js'      
    },
}

This will generate 2 files: outputone.js and outputtwo.js in the target folder.

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Leona Eden
  • 29
  • 2
0

One can also use gatsby a react framework dedicated to static multipage apps.

mquantin
  • 1,085
  • 8
  • 23