When using Vue or Nuxt, we have are spoilt for choice when it comes to asset handling: Do we put them in the
assets
folder or should we rather utilize the
public
folder? Depending on the purpose and requirements for the image, this decision can be the one or the other. In this article, we focus on one specific use case though: What if we want to load images (or other assets)
dynamically
?
Read further to find out why loading static assets from neither folder is no problem at all and which pattern to use when the asset path or name must be dynamic.
In case you want to skip the internals and explanations, you can do so and go straight to
the solution
, but you will miss out some in-depth information!
Let's define our experimental use case first: Imagine a component called
Doggo.vue
which should display the image of a cute puppy.
I mean, we
all love puppies
, don’t we 🐕️?
The only thing our components needs is a template with a single image tag using the desired path as
src
attribute.
When using the
assets
folder, you can either use relative paths or an alias like
@
or
~
, which comes pre-configured in Nuxt:
via alias
<img src="@/assets/doggos/riley.jpg">
relative path
<img src="../assets/doggos/riley.jpg">
In case you are using the
public
folder, the files will be mapped to your domain eventually, so you can omit the
public
:
<img src="/doggos/riley.jpg">
So far so good -- but what if we have a
list of cute puppies
and the user can
decide which image to display on the page
?
And it works! We can now select a puppy and the image will be displayed. Open
this CodeSandbox
to see the code up and running.
This approach has a few downsides though:
Caching: The image will be cached by the browser, so if we want to change the image, we have to change the file name as well. This is not optimal, especially if we want to use the same image in multiple places.
Image optimization: The image will not be optimized by Vite plugins or similar, so we have to do it manually.
Image size: The image will be loaded in its original size, which can be a problem for mobile users with a slow connection if the image is big.
Let's take a look how the approach for the
assets
folder looks like:
It means that the
asset path hasn’t been replaced
. It is the string that the expression in our template string above evaluates to, but no bundler magic happens.
The solution to this problem depends on the bundler you are using. If you are using Webpack with Vue 3, which is rather uncommon, you can follow the solution from the
Vue 2 / Nuxt 2 post
.
We will focus on Vite here, as it is the default bundler for Vue 3 and Nuxt 3.
As Vite does not support
require
as you might be used to if you've used webpack before, a "simple" solution with
require
is not possible.
We have to use a different approach.
Instead of
require
, we can use
import.meta.glob
. It is a special vite function that allows us to import multiple files at once. We can use it to import all images from a folder and then use the image name as key to access the image.
Let's start simple and grab all
.jpg
files from the
@/assets/doggos
folder, where our images are located.
It is very important to be as strict as possible, otherwise you can end up with a lot of files you don't want to import, harming performance of the application.
If we stringify the result, we can see that we get an object with the local image path as key and the image path in a nested object as value.
The
default
key exists, because
import.meta.glob
is used for module imports of any kind.
This is close to what we want, so we need to do some transformations.
We will take the entries of the object and map over them, eventually assembling them into an object again.
In the
map
function, we ensure to get rid of the nested object, to create a
{ filename: path }
structure.
to extract the filename from the path, we can use the
filename
function from
pathe
's utils
.
Both approaches have their pros and cons as you can see.
I'd recommend using the
asset
folder approach only for static images which might change often in the future.
The
public
folder approach is a good default solution, especially if you use the
Nuxt image module
to optimize your images. The module actually
requires
the images to be in the
public
folder.
Also, this approach is the easiest solution to implement, as you don't have to do any extra work.