Leveraging Webpack to import all images from one folder
By: Shyianovska Nataliia
Tags: JavaScript, Web Applications, Webpack, Image Loading, Vue

Did you know that apart from import and require there are some Webpack specific methods that you can use directly in your components to import files you need? Let me tell you the story of how I figured it out.

Using Require to load images

In our codebase, we had a component that rendered some logos in the footer.

It looked something like this:

And the code looked like this:

<template>

<div class="_game-provider-logos">

<div class="container-fluid">

<b-img-lazy :src="logoNetent" alt="Netent Logo" fluid />

<b-img-lazy :src="logoYggrasil" alt="Yggrasil logo" fluid />

<b-img-lazy :src="logoScientificGames" alt="Scientific Games logo" fluid />

<b-img-lazy :src="logoMicrogaming" alt="Microgaming logo" fluid />

<b-img-lazy :src="logoSlingo" alt="Slingo logo" fluid />

</div>

</div>

</template>



<script>

export default {

name: 'FooterGameProviders',

created () {

this.logoMicrogaming = require(`@/assets/images/footer/microgaming.png`)

this.logoNetent = require(`@/assets/images/footer/netent.png`)

this.logoScientificGames = require(`@/assets/images/footer/scientific-games.png`)

this.logoSlingo = require(`@/assets/images/footer/slingo.png`)

this.logoYggrasil = require(`@/assets/images/footer/yggdrasil.png`)

}

}

</script>

So, as you can see it was a pretty much dummy component. It just imported some images from the asset folder using require function and displayed them using the bootstrap <b-img-lazy /> component.

The problem

One day I came across a ticket related to this component. The task was to simply add another 19 logos to it. Pretty straightforward, right? And I already imagined how I manually do the same thing 19 times, just like a robot … But before actually doing that I decided to double-check if there is a way to somehow automate this process.

And here is where Webpack’s require.context came to help. Since most of the modern javascript applications are using Webpack under the hood and some image loaders alongside why actually not going a bit further and trying to use Webpack specific methods to load required assets?

Let’s check how it works.

What is require.context

According to Webpack documentation:

require.context allows you to pass in a directory to search, a flag indicating whether subdirectories should be searched too, and a regular expression to match files against.

The syntax is as follows:

require.context(

directory,

(useSubdirectories = true),

(regExp = /^\.\/.*$/),

(mode = 'sync')

)

An example for my case will look like this:

require.context('./../assets/images/footer/game-providers', false, /\.png$/)

You can play with this depending on the type of files you need(it can be components, HTML files, styles, or anything else), and whether you need files from subdirectories or not.

Context module API

Let’s have another look into docs:

A context module exports a (require) function that takes one argument: the request. The exported function has 3 properties: resolve , keys , id . resolve is a function and returns the module id of the parsed request. keys is a function that returns an array of all possible requests that the context module can handle. id is the module id of the context module.

In our case the most useful will be the keys property which we can use to import all files from the images folder like this:

function importAll(r) {

r.keys().forEach(r)

}



importAll(require.context('../assets/images/', true, /\.jpg\.svg\.png$/))

Important to note that Webpack parses for require.context in the code while building and all the arguments passed to require.context must be literals.

The solution

After adding all necessary images into a separate folder, and applying the guidelines from above the component started to look as follows:

<template>

<div class="_game-provider-logos">

<div class="container-fluid">

<b-img-lazy

v-for="image in images"

:key="image"

:src="image"

fluid

/>

</div>

</div>

</template>



<script>

export default {

name: 'FooterGameProviders',

computed: {

images () {

const path = require.context('./../assets/images/footer/game-providers', false, /\.png$/)

return path.keys().map(path)

}

}

}

</script>

As you can see now it is even smaller than it was with the 5 images. No repeating anything many times, no need to type file names and in general, it looks just great.

Amazing stuff, isn’t it?