Skip to content

Adding asset files to webpack

Let’s see how to configure webpack to manage general assets, for example, text and pdf files. We’ll see how to configure webpack to dynamically generate URLs for these files. As an application, we’ll show how to add download links to these resources.

Project structure and setup

Our project will have the following folder structure:

.
├── build
├── index.html
├── package.json
├── package-lock.json
├── src
│   ├── assets
│   │   ├── file.pdf
│   │   └── file.txt
│   └── index.js
└── webpack.config.js

First, we initialize an npm project. From the project root folder:

$ npm init -y

Then, we add the dependencies we’ll need:

$ npm install -D webpack webpack-cli html-webpack-plugin

index.html will be our main HTML page. Its body tag content will consist of the following anchor tags:

<!-- index.html -->
<body>
  <a id="pdf-file" target="_blank">Download PDF</a>
  <br />
  <a id="txt-file" target="_blank">Download TXT</a>
</body>

These will be used as links to download file.pdf and file.txt.

Next, let’s see the required webpack configuration to add links to these files to the HTML file.

Basic webpack configuration

Webpack takes its configuration from webpack.config.js. Let’s add the minimal configuration to process HTML files and asset files (pdf, txt, etc).

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    index: "./src/index.js",
  },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "build"),
    assetModuleFilename: "assets/[name][ext]",
    clean: true,
  },
  mode: "development",
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "index.html"),
      filename: "index.html",
      chunks: ["index"],
    }),
  ],
  module: {
    rules: [
      {
        test: /\.(pdf|txt)$/,
        include: path.resolve(__dirname, "src"),
        type: "asset/resource",
        generator: {
          filename: "assets/[name][ext]",
        },
      },
    ],
  },
};

With this configuration, we area defining a single entry point (src/index.js), which is in turn linked to index.html using the webpack-html-plugin (see webpack configuration for multiple entry points).

Besides HTML and JavaScript files, we define special rules for pdf and txt files. These will be all loaded with webpack’s built-in asset modules, in particular with asset/resource. Note that these modules are present only in webpack 5.

Let’s study the configuration options that are specific to assets.

First, we set a directory to store these files. This is done with the output.assetModuleFilenames property. Note that this path is relative to our main output path (build/, in this case).

assetModuleFilename: "assets/[name][ext]"

The special [name] and [ext] placeholders will be replaced by the name and extension of each file. For example, loading file1.txt to our project will copy it to build/assets/file1.txt. The resulting URL address once uploaded to a server would be: mywebsite.com/assets/file1.txt.

Processing these files is done by the asset/resource module following this rule:

module: {
  rules: [
    {
      test: /\.(pdf|txt)$/,
      include: path.resolve(__dirname, "src"),
      type: "asset/resource",
    },
  ],
}

Both pdf and txt files (we could add other file extensions, too) will match and be processed by asset/resource. It will copy the matching files to the specified output and replace all references to these files in our source code with their corresponding relative URL path.

With this configuration, let’s see how to add references to our assets.

Adding asset files using scripts

First, let’s add assets using JavaScript.

Remember that we configured index.js to be linked to index.html.

// index.js
import PDF from "./assets/file.pdf";
import TXT from "./assets/file.txt";
// const PDF = require("./assets/file.pdf");
// const TXT = require("./assets/file.txt");

document.getElementById("pdf-file").href = PDF;
document.getElementById("txt-file").href = TXT;

See that this script imports the assets from src/assets just like any other JavaScript module. We can use these import references as we would with any variable. Once webpack processes our source code, it will replace these references with the correct relative URLs, following our specified configuration.

We can test this by bundling the files. Either define an npm script that runs webpack or run the following:

$ npx webpack

It will write the output files to build/. First, we notice that the assets have been copied to build/assets.

build/
├── assets
│   ├── file.pdf
│   └── file.txt
├── index.html
└── index.js

If we open build/index.html with a web browser, we’ll see the anchor tag references have been changed to the corresponding URL paths of the assets!

Adding asset files directly to HTML files

Using scripts to load assets is completely functional and it’s the approach we would use with frameworks like React. However, we can also load assets dynamically without using scripts at all. To do so, we’ll need an HTML processor. We’ll use pug for this demo, but any processor would do.

First, we need to configure webpack to process pug files. We’ll use simple-pug-loader, which works better with webpack 5.

We install the module.

$ npm i -D simple-pug-loader

And then we a add the following configuration to webpack.config.js:

// webpack.config.js
...
module.exports = {
  ...
  plugins: [
    ...
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "pugindex.pug"),
      filename: "pugindex.html",
    }),
  ],
 module: {
    rules: [
      ...
      {
        test: /\.pug$/,
        include: path.resolve(__dirname),
        use: [
          {
            loader: "simple-pug-loader",
          },
        ],
      },
    ],
  },
};

The two single changes were: 1) configuring the html-webpack-plugin to process pugindex.pug and generate pugindex.html, 2) adding a new rule to process all .pug files with simple-pug-loader.

With this new configuration, let’s create pugindex.pug at the root folder:

// pugindex.pug
doctype html
html(lang="en")
  head
    meta(charset="UTF-8")
    meta(http-equiv="X-UA-Compatible", content="IE=edge")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    title Webpack Files
  body
    p Hello world!

Let’s build the files to see it all works.

Adding dynamic file references to pug

Finally, we can add references in pug to asset files as follows:

// pugindex.pug
...
body
  a(href=require("./src/assets/file.pdf") target="_blank") Download pdf
  br
  a(href=require("./src/assets/file.txt") target="_blank") Download txt 

We add the references with the NodeJS require keyword used to load modules. We pass to it the path of the asset relative to the pug file. With the webpack configuration of the previous sections, these references will be processed to add relative URLs to the locations these files will be copied to after bundling by wepback!

One of the advantages of this method is that we don’t need to manually keep track of the assets and copy them to our public folder. We can load assets from any path and webpack will process all of these references to the desired folder structure for our webpage.

Published inProgramming
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
reyescult
1 year ago

Super helpful. Thank you!