Introduction
Create React App (CRA) is beloved for its zero‑configuration setup, but developers often hit a wall when they need to serve more than one HTML page from the same codebase. Traditional single‑page applications (SPAs) rely on a single entry point (usually src/index.js), which makes it cumbersome to build distinct sections such as an admin panel, a marketing landing page, or a client‑specific dashboard. Fortunately, CRA can be extended to support multiple entry points without the risky step of “ejecting” the underlying webpack configuration. This article walks you through the rationale, the required tooling, the precise configuration tweaks, and the deployment considerations needed to turn a standard CRA project into a clean, maintainable multi‑page React application.
Why Multiple Entry Points Matter
When a project grows, separating concerns at the build level brings several advantages:
- Independent bundles: Each page loads only the JavaScript and CSS it truly needs, improving initial load time.
- Tailored HTML templates: Different pages can have unique meta tags, favicons, or third‑party scripts without polluting a single
index.html. - Clearer code organization: Entry‑specific root components keep the component tree shallow and easier to reason about.
- Simplified CI/CD: Deployments can target distinct URLs (e.g.,
/adminvs./) while sharing the same repository and build pipeline.
Understanding these benefits helps you decide whether a multi‑page approach fits your architecture before you start tinkering with the build system.
Setting Up the Project and Adding a Second Entry
Begin with a fresh CRA project and install a lightweight override tool that lets you modify webpack without ejecting:
- Run
npx create-react-app multi‑entry-demo. - Install
react-app-rewiredandcustomize-craas dev dependencies: npm i -D react-app-rewired customize-cra
Next, adjust the scripts in package.json to use the rewired binary:
"start": "react-app-rewired start""build": "react-app-rewired build"
Create a new entry file, for example src/admin.js, and a matching HTML template public/admin.html. The template should contain a distinct div with an id that matches the root element rendered by admin.js (e.g., <div id="admin-root"></div>).
Extending the Webpack Config Without Ejecting
Now create config-overrides.js at the project root. Using customize‑cra, you can inject additional entry points and HTML plugins while preserving CRA’s defaults:
- Import the required helpers:
const { override, addWebpackPlugin } = require('customize-cra');- Import
HtmlWebpackPluginto generate the second HTML file: const HtmlWebpackPlugin = require('html-webpack-plugin');- Define a function that merges the new entry:
const addAdminEntry = () => config => {- config.entry = { …config.entry, admin: ‘./src/admin.js’ };
- return config;
};- Export the composed overrides:
module.exports = override(- addAdminEntry(),
- addWebpackPlugin(new HtmlWebpackPlugin({
- template: ‘./public/admin.html’,
- filename: ‘admin.html’,
- chunks: [‘admin’]
- }))
- );
This configuration tells CRA to bundle admin.js separately and to emit an admin.html file that loads only the admin chunk. Because we never ran npm run eject, all future CRA updates remain compatible.
Building, Deploying, and Best Practices
Run npm run build and you will see two HTML files (index.html and admin.html) alongside their respective JavaScript bundles in the build folder. When deploying:
- Serve
index.htmlat the root URL (e.g.,example.com/). - Serve
admin.htmlat a sub‑path (e.g.,example.com/admin/) or a separate sub‑domain. - Configure your web server to fallback to the correct HTML file for client‑side routing (e.g.,
RewriteRule ^admin/ - [L]for Apache).
Additional tips to keep the setup robust:
- Lazy‑load shared components: Use dynamic
import()so that common code is extracted into a shared chunk. - Environment variables: Prefix variables with
REACT_APP_and reference them in each entry to keep configuration consistent. - Testing: Run the development server with
npm start; CRA will automatically serve both pages athttp://localhost:3000/andhttp://localhost:3000/admin.html.
By following these patterns, you maintain the simplicity of CRA while gaining the flexibility of a true multi‑page application.
Conclusion
Building a multi‑page React app with multiple entry points is entirely feasible inside the Create React App ecosystem, provided you leverage a non‑ejecting override tool such as react-app-rewired. The process involves adding extra entry files, creating dedicated HTML templates, and modestly extending the webpack configuration through config-overrides.js. The result is a set of independent bundles that load only the code each page requires, leading to faster performance, cleaner separation of concerns, and smoother deployment pipelines. By adhering to best practices—lazy‑loading shared modules, using environment variables wisely, and configuring server fallbacks—you can scale your project without sacrificing the convenience of CRA’s managed setup. This approach empowers teams to deliver richer, multi‑faceted experiences while keeping maintenance overhead low.









