I have a page with 20% of React components and 80% of normal html content. It is necessary to place several react components at different points of the page, and therefore I need several entry points in index.html. I want to use create-react-app, but I don’t want to use redux, since the page is rather small. Are there ways it’s relatively easy to make 2 entry points in index.html so that ALL react components on the page have access to the single parent's props? .. Or is it possible to use global variables with an event listener for their changes that would triger update the react components in different entry points? .. Please tell me the best practice for such tasks, because I don't want to develop a whole page by using jsx from a single entry point.
3 Answers
To avoid ejecting you might want to check rescripts, you can add entry points to be added to index.html like so:
create .rescriptsrc.js file in projects main directory:
module.exports = [
config => {
config.entry = {
app: ["./src/index.js"],
content: ["./src/content.js"],
};
}
];

- 1,443
- 1
- 16
- 36
-
1I have some problems not defining `main` in the entries, rescripts gives me an error, but defining it as dummy/stub and manipulating the webpack configuration a bit I managed to get the result I was looking for. – Alex Mar 29 '20 at 01:28
-
@Alex might wanna create an issue on their github, the developer is very helpful – K41F4r Mar 29 '20 at 10:09
-
I asked for a few examples using multiple entries but they advised me to post here. I think the problem is more in the CRA infrastructure, there is an open issue to support multiple entries but it is taking time. – Alex Mar 29 '20 at 11:15
I know it's a delayed answer, but just for future searches, the steps are:
- Eject (
yarn eject
) - Edit paths.js and add the second entry point html file under the entry for appHtml
appAdminHtml: resolveApp('public/admin.html'),
- Update entry inside
webpack.config.js
to include one entry per entry point.
entry: {
index: [
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
].filter(Boolean),
admin: [
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appSrc + '/admin/index.js',
].filter(Boolean)
},
- Change the generated output JS file to the name of the entry (inside
webpack.config.js
)
output: {
path: isEnvProduction ? paths.appBuild : undefined,
pathinfo: isEnvDevelopment,
// This is the important entry
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/[name].bundle.js',
futureEmitAssets: true,
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
publicPath: publicPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
globalObject: 'this',
},
- Update the plugins to generate the second file with the injected js script (also inside
webpack.config.js
).
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
chunks: ['index'],
template: paths.appHtml,
filename: 'index.html'
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Generates an `admin.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
chunks: ['admin'],
template: paths.appAdminHtml,
filename: 'admin.html',
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
- Update the
ManifestPlugin configuration to include the new entry point (also inside
webpack.config.js`):
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
let entrypointFiles = [];
for (let [entryFile, fileName] of Object.entries(entrypoints)) {
let notMapFiles = fileName.filter(fileName => !fileName.endsWith('.map'));
entrypointFiles = entrypointFiles.concat(notMapFiles);
};
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
- Update your server (both dev and prod) to rewrite paths.
- For the dev server, you need to update
webpackDevServer.config.js
.
- For the dev server, you need to update
historyApiFallback: {
disableDotRule: true,
verbose: true,
rewrites: [
{ from: /^\/admin/, to: '/admin.html' },
]
},
Since Prod server settings can be quite different, I'll let you figure it out.
This post describes everything in more detail.

- 10,480
- 6
- 33
- 43
Adding multiple entry points require modifying the default react-scripts
configuration. Ejecting (i.e. extracting all configuration from react-scripts
and manage them yourself) is one way to do so.
Ejecting lets you customize anything, but from that point on you have to maintain the configuration and scripts yourself. This can be daunting if you have many similar projects. In such cases instead of ejecting we recommend to fork react-scripts and any other packages you need
Please visit https://create-react-app.dev/docs/alternatives-to-ejecting for details.
When I came across this problem, I created a fork of the script and made it available at https://www.npmjs.com/package/@linsight/react-scripts. Please try it out.
Do remember updating the react-app-env.d.ts
file to:
/// <reference types="@linsight/react-scripts" />

- 13,168
- 5
- 46
- 46