232

I am trying to automate assets going into /dist. I have the following config.js:

module.exports = {
  context: __dirname + "/lib",
  entry: {
    main: [
      "./baa.ts"
    ]
  },
  output: {
    path: __dirname + "/dist",
    filename: "foo.js"
  },
  devtool: "source-map",
  module: {
    loaders: [
      {
        test: /\.ts$/,
        loader: 'awesome-typescript-loader'
      },
      { test: /\.css$/, loader: "style-loader!css-loader" }
    ]
  },
  resolve: {
    // you can now require('file') instead of require('file.js')
    extensions: ['', '.js', '.json']
  }
}

I also want to include main.html from the directory that sits next to /lib, into the /dist folder when running webpack. How can I do this?

UPDATE 1 2017_____________

My favourite way to do this now is to use the html-webpack-plugin with a template file. Thanks to the accepted answer too! The advantage of this way is that the index file will also have the cachbusted js link added out of the box!

Community
  • 1
  • 1
SuperUberDuper
  • 9,242
  • 9
  • 39
  • 72

11 Answers11

213

Option 1

In your index.js file (i.e. webpack entry) add a require to your index.html via file-loader plugin, e.g.:

require('file-loader?name=[name].[ext]!../index.html');

Once you build your project with webpack, index.html will be in the output folder.

Option 2

Use html-webpack-plugin to avoid having an index.html at all. Simply have webpack generate the file for you.

In this case if you want to keep your own index.html file as template, you may use this configuration:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
}

See the docs for more information.

Sergiy Ostrovsky
  • 2,372
  • 2
  • 16
  • 23
VitalyB
  • 12,397
  • 9
  • 72
  • 94
  • 2
    can i somehow load it writing something in config file itself ? – codeVerine May 10 '16 at 10:29
  • Tried the first way, and it copied the files. But the CSS that I was copying stopped working correctly. (I needed it external to webpack because Handsontable can't work with webpack.) – Vaccano Jul 27 '16 at 17:51
  • @Vaccano for CSS you shouldn't use this method. Use a style-loader and css-loader, see here: http://stackoverflow.com/questions/34039826/webpack-style-loader-vs-css-loader – VitalyB Jul 28 '16 at 08:47
  • the second option is really convenient :) – João Andrade Oct 21 '16 at 09:45
  • 4
    In webpack v2 you apparently can't omit the `-loader` suffix. e.g. `require('file-loader?name=[name].[ext]!../index.html');` – overthink Feb 04 '17 at 18:40
  • 1
    @codeVerine Yes, using by adding something like `{ test: /index\.html/, loader: 'file-loader', query: { name: '[name].[ext]' }` to your `loaders` array in your webpack config file, only I couldn't get webpack-dev-server to serve it, leading to, funnily enough, a 404 in requesting `/` (the root doesn't exist!). – Brian McCutchon Feb 06 '17 at 03:36
  • Thank you for the 2nd option. – Lance Kind Aug 14 '17 at 07:17
  • @BrianMcCutchon adding that to my `webpack.config.js` worked perfectly for me. if you add it as a separate answer, I'll upvote it – c2huc2hu Oct 12 '17 at 05:23
  • @overthink Thanks. Updated. – VitalyB Aug 07 '18 at 06:35
  • 1
    sorry my newbie question, but how can webpack generate an index.html for me if my index.html is quite complex with a lot divs and a big structure? – João Pimentel Ferreira Dec 25 '20 at 22:21
  • 1
    @JoãoPimentelFerreira You provide the template of the HTML you want to generate. – VitalyB Jan 11 '21 at 15:38
  • 1
    option 2 need to install `npm install html-webpack-plugin` and then put it `const HtmlWebpackPlugin = require('html-webpack-plugin');` into `webpack.config.js` file. – Nabi K.A.Z. Feb 20 '22 at 02:23
76

I will add an option to VitalyB's answer:

Option 3

Via npm. If you run your commands via npm, then you could add this setup to your package.json (check out also the webpack.config.js there too). For developing run npm start, no need to copy index.html in this case because the web server will be run from the source files directory, and the bundle.js will be available from the same place (the bundle.js will live in memory only but will available as if it was located together with index.html). For production run npm run build and a dist folder will contain your bundle.js and index.html gets copied with good old cp-command, as you can see below:

"scripts": {
    "test": "NODE_ENV=test karma start",
    "start": "node node_modules/.bin/webpack-dev-server --content-base app",
    "build": "NODE_ENV=production node node_modules/.bin/webpack && cp app/index.html dist/index.html"
  }

Update: Option 4

There is a copy-webpack-plugin, as described in this Stackoverflow answer

But generally, except for the very "first" file (like index.html) and larger assets (like large images or video), include the css, html, images and so on directly in your app via require and webpack will include it for you (well, after you set it up correctly with loaders and possibly plugins).

Community
  • 1
  • 1
EricC
  • 5,720
  • 13
  • 52
  • 71
43

You could use the CopyWebpackPlugin. It's working just like this:

module.exports = {
  plugins: [
    new CopyWebpackPlugin([{
      from: './*.html'
    }])
  ]
}
hobbeshunter
  • 629
  • 6
  • 6
  • Now that Webpack has replaced Gulp and Grunt by not just doing bundling, but also many other build-related tasks, this solution is what I have seen in most projects. Scripts in `package.json` are only used for simple things like starting the test runner or the dev server. – Robert Jack Will Oct 18 '19 at 15:17
  • This doesn't seem to automatically reload when changes are made to the original files, am I missing something? – TCB13 Apr 19 '23 at 10:09
16

I would say the answer is: you can't. (or at least: you shouldn't). This is not what Webpack is supposed to do. Webpack is a bundler, and it should not be used for other tasks (in this case: copying static files is another task). You should use a tool like Grunt or Gulp to do such tasks. It is very common to integrate Webpack as a Grunt task or as a Gulp task. They both have other tasks useful for copying files like you described, for example, grunt-contrib-copy or gulp-copy.

For other assets (not the index.html), you can just bundle them in with Webpack (that is exactly what Webpack is for). For example, var image = require('assets/my_image.png');. But I assume your index.html needs to not be a part of the bundle, and therefore it is not a job for the bundler.

cxw
  • 16,685
  • 2
  • 45
  • 81
Brodie Garnet
  • 432
  • 2
  • 8
  • 66
    I precisely went to webpack so I wouldn't need to use grunt or gulp. Is there any other alternative? If I need to use gulp why should I bother with webpack? – SuperUberDuper Aug 22 '15 at 10:49
  • 5
    The question is upside down. Why should you use webpack if you can use grunt or gulp? They are very good task/build systems. Webpack (or browserify or r.js) are tools you can use for bundling lots of JS-files (and other resources) into one big (or multiple) javascript bundles. You should use the correct tool for the job. And again, it is very common to run webpack, browserify or other bundlers from grunt or gulp. – Brodie Garnet Aug 22 '15 at 10:55
  • how can you put an image into a js file? – SuperUberDuper Aug 22 '15 at 10:59
  • 1
    There are many ways webpack can do that. You could use `file-loader` which basically just copies the file/image to the output directory and gives you the url when you requires it: `var url = require('myFile');`. As I said, a bundle can be one _or multiple_ files. – Brodie Garnet Aug 22 '15 at 11:08
  • For really small assets (like a little icon or something) it is probably faster if it just bundles the image as a string in javascript. Images (and other files) can be encoded as a string (for example by using a [data uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs) ). See for example [here](http://appcropolis.com/blog/web-technology/javascript-encode-images-dataurl/) for how to encode a image. There are different loaders for different kind of files in webpack, and they does this in different ways. – Brodie Garnet Aug 22 '15 at 11:08
  • 1
    I might use brocolli as the parent build process – SuperUberDuper Aug 22 '15 at 11:18
  • 1
    This is the right answer to me. In large/complex projects, webpack build performance is an important consideration. Various file copy plugins adds unnecessary cost to webpack, letting webpack focus on JS bundling is a better idea. – Evi Song Aug 28 '16 at 13:31
  • I consider stupid using other tools if a bundler (webpak) can not bundle a static file ! – sancelot Aug 03 '17 at 13:17
  • whatever your build script is, add a final line which does "cp ./src/index.html ./dist" – Gianluca Ghettini Nov 17 '20 at 11:12
15

To copy an already existing index.html file into the dist directory you can simply use the HtmlWebpackPlugin by specifying the source index.html as a template.

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // ...
  plugins: [    
    new HtmlWebpackPlugin({
      template: './path/to/index.html',
    })
  ],
  // ...
};

The created dist/index.html file will be basically the same as your source file with the difference that bundled resources like .js files are injected with <script> tags by webpack. Minification and further options can be configured and are documented on github.

Tobi Obeck
  • 1,918
  • 1
  • 19
  • 31
  • That's much better than any kind of copy plugin.. `HtmlWebpackPlugin` is able to generate `index.html` by itself if you need. So, no need to create almost empty html page for SPA yourself. – hastrb Oct 23 '22 at 15:44
14

You can add the index directly to your entry config and using a file-loader to load it

module.exports = {

  entry: [
    __dirname + "/index.html",
    .. other js files here
  ],

  module: {
    rules: [
      {
        test: /\.html/, 
        loader: 'file-loader?name=[name].[ext]', 
      },
      .. other loaders
    ]
  }

}
Jake Coxon
  • 4,958
  • 1
  • 24
  • 15
7

To extend @hobbeshunter's answer if you want to take only index.html you can also use CopyPlugin, The main motivation to use this method over using other packages is because it's a nightmare to add many packages for every single type and config it etc. The easiest way is to use CopyPlugin for everything:

npm install copy-webpack-plugin --save-dev

Then

const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyPlugin([
      { from: 'static', to: 'static' },
      { from: 'index.html', to: 'index.html', toType: 'file'},
    ]),
  ],
};

As you can see it copy the whole static folder along with all of it's content into dist folder. No css or file or any other plugins needed.

While this method doesn't suit for everything, it would get the job done simply & quickly.

Remy
  • 1,053
  • 1
  • 19
  • 25
4

This work well on Windows:

  1. npm install --save-dev copyfiles
  2. In package.json I have a copy task : "copy": "copyfiles -u 1 ./app/index.html ./deploy"

This move my index.html from the app folder into the deploy folder.

Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341
  • I get it works using answer there: https://stackoverflow.com/questions/38858718/npm-script-copy-package-json-to-dist-when-bundling/51371023#51371023 – IsraGab Jul 16 '18 at 22:48
0

Webpack 5 comes with asset modules so you don't need any copy-plugins or file-loader anymore

Arek C.
  • 1,271
  • 2
  • 9
  • 17
0

Ok you can copy files by npm installing copy-webpack-plugin. The version i am using here is 11.0.0.

Below the instructions tell webpack that the source js file is at ./src/index.js. The directory to build assets to is the 'dist' dir. Then at the bottom CopyPlugin copies all html files from 'src' dir to 'dist' dir:

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "./src/*.html",
          to({ context, absoluteFilename }) {
            return "[name][ext]";
          },
        },
      ],
    }),
  ]
};
omarjebari
  • 4,861
  • 3
  • 34
  • 32
-1

I also found it easy and generic enough to put my index.html file in dist/ directory and add <script src='main.js'></script> to index.html to include my bundled webpack files. main.js seems to be default output name of our bundle if no other specified in webpack's conf file. I guess it's not good and long-term solution, but I hope it can help to understand how webpack works.

Qback
  • 4,310
  • 3
  • 25
  • 38