18

I want to build a react app and have the static css and js files effectively embedded into the index.html that gets produced when I call yarn build.

I can obviously hack the output and replace the <script src"... and <link href="/static/css/... tags with inline versions but was hoping for a more elegant solution.

pomo
  • 2,061
  • 18
  • 32

4 Answers4

23

I got it working. For future reference (using react 16.7.0 & yarn 1.10.1)...

Create a react app:

npx create-react-app my-app
cd my-app
yarn build

All good? Cool, run eject so that you have access to the webpack config:

yarn eject
rm -rf build node_modules
yarn install
yarn build

Add this project and update webpack:

yarn add html-webpack-plugin@4.0.0-beta.4
yarn add html-webpack-inline-source-plugin@1.0.0-beta.2

Edit config/webpack.config.js:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); <--- add this
...
plugins: [
  new HtmlWebpackPlugin(
    Object.assign(
      ...
      isEnvProduction
        ? {
            inlineSource: '.(js|css)$', <-- add this
            minify: {
              ...
  ),
  ...
  isEnvProduction &&
    shouldInlineRuntimeChunk &&
    new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin), <--- add this
    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
    ...

Run rm -rf build && yarn build && serve -s build - the index.html you load should now contain all the js and css stuff inline. Note that the static files are still created in the build dir, but they aren't used by the page.

pomo
  • 2,061
  • 18
  • 32
7

There is a Webpack plugin that has been created for this purpose:

https://www.npmjs.com/package/html-webpack-inline-source-plugin

As the README specifies, this must be used with html-webpack-plugin, and you have to specify the inlineSource that is passed in:

plugins: [
  new HtmlWebpackPlugin({
        inlineSource: '.(js|css)$' // embed all javascript and css inline
    }),
  new HtmlWebpackInlineSourcePlugin()
]
Kevin Hoerr
  • 1,941
  • 1
  • 9
  • 17
  • Thanks but I can't get this working. Have added html-webpack-inline-source-plugin and html-webpack-plugin to my devDependencies, and have created a webpack.config.js file (though not sure exactly what needs to be in there). But the build ignores it. – pomo Jan 03 '19 at 17:24
  • Ah, it was my assumption you were using webpack already. Do you know what's compiling it under the hood? Are you using something like create-react-app? – Kevin Hoerr Jan 04 '19 at 16:21
  • An update in 2021, we can replace `new HtmlWebpackInlineSourcePlugin()` to `new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin)` to get it work. – keikai May 23 '21 at 15:32
5

Previous answers don't work because html-webpack-inline-source-plugin is no longer supported by the author, replaced by official plugins html-inline-script-webpack-plugin and html-inline-css-webpack-plugin. The following method works with the latest React 17 as of October 2021. Steps:

Create a React app

create-react-app my-app
cd my-app
yarn
yarn start

Make sure it's working, you're happy with the output. Then, eject your config (I think this can be done without ejecting config, but I can't be bothered to look up how to configure CRA)

yarn eject
rm -rf build node_modules
yarn
yarn build

Then, add the Webpack plugins (Assuming you want both CSS and scripts embedded here, just remove one if you only want the other)

yarn add html-inline-css-webpack-plugin -D
yarn add html-inline-script-webpack-plugin -D

Finally, add these to the Webpack config config/webpack.config.js. Firstly declare them at the top of the file:

const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default;
const HtmlInlineScriptPlugin = require('html-inline-script-webpack-plugin');

Then add them to the plugins: [...] collection. Search for new InlineChunkHtmlPlugin and add them just after that:

      isEnvProduction &&
        shouldInlineRuntimeChunk &&
        new HTMLInlineCSSWebpackPlugin(),
      isEnvProduction &&
        shouldInlineRuntimeChunk &&
        new HtmlInlineScriptPlugin(),

Note: It is important to include the isEnvProduction / shouldInlineRuntimeChunk checks! If you skip these the hot refresh won't work when you run yarn start. You only want to embed the scripts in production mode, not during development.

Now, if you run yarn build you'll find all the CSS and JS embedded in build/index.html. If you run yarn start it'll start a hot-reloading dev environment you can develop in.

Jansky
  • 1,242
  • 16
  • 29
  • After performing these steps it looks to have inlined everything into my index.html file correctly, however, the release index.html file in the build directory doesn't load properly now and just shows a blank white screen. Any idea what could be the issue here? I am very much a newbie to the this world. – not an alien Mar 16 '22 at 17:17
  • 2
    You probably have to add this to your HtmlWebpackPlugin configuration inside your webpack.config.js. Look for the `inject` property and change it's value to `body`. This makes sure that your JavaScript is loaded after your HTML is done loading. The issue your probably having is that your script is loaded before your HTML is ready. – Stanley Thijssen Mar 17 '22 at 01:26
  • Thanks @StanleyThijssen, I wasn't able to change the `inject` property's value to `body` (it looks like a boolean in my project), but you were spot on the mark with your reasoning - after moving my inline script to the last line of the HTML ``, it loads as expected. Big kudos! – not an alien Mar 17 '22 at 09:58
0

I couldn't get it working with all of the supplied answers. However, I want to help anyone trying to find the solution by providing some links that I found corresponding to this issue:

https://github.com/facebook/create-react-app/issues/3365#issuecomment-376546407

Generate single physical javascript file using create-react-app

Inline CSS with Webpack, without HtmlWebpackInlineSourcePlugin?

https://pangyiwei.medium.com/building-a-react-app-as-a-single-html-file-with-create-react-app-no-eject-283a141e7635

https://www.labnol.org/code/bundle-react-app-single-file-200514

For my very simple use case minification etc. is not as important so I will just use this modified version of the react website example (example download here).

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>Add React in One Minute</title>
</head>

<body>

  <h2>Add React in One Minute</h2>
  <p>This page demonstrates using React with no build tooling.</p>
  <p>React is loaded as a script tag.</p>

  <!-- We will put our React component inside this div. -->
  <div id="root"></div>

  <!-- Load React. -->
  <!-- Note: when deploying, replace "development.js" with "production.min.js". -->
  <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

  <script type="text/babel">
    'use strict';
    class LikeButton extends React.Component {
      constructor(props) {
        super(props);
        this.state = { liked: false };
      }

      render() {
        if (this.state.liked) {
          return 'You liked this.';
        }

        return <button onClick={() => this.setState({ liked: true })} > Like </button >;
      }
    }
    const root = ReactDOM.createRoot(document.getElementById('root'));

    root.render(<LikeButton />)
  </script>
</body>

</html>

Hope this helps

J.K.
  • 126
  • 5