Why must images be imported for Webpack?
As you migrate from legacy, vanilla code systems towards advanced build systems and single-page applications, you may notice some changes that don’t make immediate sense. There are additional steps that are a bit unintuitive, and it’s not clear what problem is being solved. One such difference is that
<img src="./file.gif" /> no longer works. You now have to use
import image from './file.gif';. Why?
With a build system like Webpack, your application is no longer running in the same directory as your HTML, e.g. your
src directory. It goes through a "build" process that first optimizes the files before outputting the final result into a different directory. Webpack typically starts with your
index.html, and that
index.html file is what you see in your browser. This finalized application runs from your
build directory in a production build or from memory in a development build, not from your
If Webpack never sees your image file, it won’t put it into the
build directory. For all it knows, it’s just an unused file, so it shouldn’t be put into the final output. Similarly, if you create a
my-file.js in your
src directory but you never use it anywhere, Webpack won't include it in the final bundle. This is a good thing! It would be a waste of space!
Webpack does not scan the
src directory for files. Webpack starts at an entry point (
index.js) and follows
require statements to find files to bundle. When you
import the image, Webpack now knows that you are using it. Webpack now “sees” that image.
<img src="..." />) and return a string with the URL of the image. You just tell it “I want
blank.gif” and it says “Okay, I’ll move this
blank.gif file to the build directory, and here is the path in the build directory:
/blank.some-unique-hashed-filename.gif”. You use that file path as your image’s
src attribute, and the browser will be able to access that file — because your browser is looking for files in the
build directory, not in
The unique hash added to the image URL solves two problems. First, it prevents
src/a/image.gif from being overwritten by
src/b/image.gif. Second, it prevents the customer from seeing an outdated cache when you update the image. If the customer views and permanently caches
image.gif that was bundled from
src/a/image.gif, then you as a developer update that image file, the customer still sees their permanently cached version. Permanent cache is a great tool to improve the loading speed and customer experience of your application. Rather than disabling permanent cache, the better alternative is to provide and change a unique hash whenever the source image file changes. Each time you build your
image.gif file, you will generate
image.unique-hash-1.gif. Once you change that source
image.gif file, it will become
image.unique-hash-2.gif, bypassing the customer’s cache of
image.unique-hash-1.gif and providing them the updated image.
You can likely get similar behavior by putting your static (unchanging, cache-friendly) files in the
public directory. The
public directory gets copied over to the build directory, almost verbatim. The only exception is that
blank.gif in your
public directory, you should be able to reference it as just
blank.gif, because — as it is copied to the build directory — you will be able to find it in the build directory. This is in contrast to files in
src which are only copied to the build directory if Webpack finds them through the entry point file (
index.js). All other things in
src are ignored.
If you have any questions or great insight into build processes, please leave them in the comments below.