Handling Assets in Nuxt 3

Nuxt 3 gives us two great options for managing all of our assets in our web app. But what’s the difference, and how do you know which to use?

Michael Thiessen
Nuxt 3

Mastering Nuxt 3 course is here!

Get notified when we release new tutorials, lessons, and other expert Nuxt content.

Click here to view the Nuxt 3 course

Nuxt 3 gives us two great options for managing all of our assets in our web app.

But what’s the difference, and how do you know which to use?

************************Short answer: use ~/assets by default, only using ~/public if you absolutely need to.

In this article I’ll share some simple questions you can ask yourself to figure out where to put your assets, as well as the differences between the two strategies.

Two Ways to Handle Assets

Not everything in your app is code.

There are lots of non-code assets:

  • images
  • icons
  • fonts
  • audio files
  • stylesheets (yes, technically it’s code, but it’s not JS)
  • etc.

In any web app, including with Nuxt, we have two main ways to deal with these non-code assets:

  1. We can process them through our bundler
  2. We can leave them as is

While both of these are ways to handle assets, the first method uses the ~/assets folder and the second uses the ~/public folder.

Yes, the naming is kind of confusing, but it’s a convention that has long been used by the broader web dev community, so we’re stuck with it.

How do you choose?

There are 3 main questions to ask yourself:

  1. Does this need to be processed in some way?
  2. Does this asset change often, or not at all?
  3. Does it need a specific filename?

1. Does this need to be processed in some way?

The first question is mostly self-explanatory.

If you have a bunch of images, you likely want to make sure those images are compressed and optimized before shipping them with your web application.

A sitemap, on the other hand, is good as it is. No need to do anything extra with it.

2. Does this asset change often, or not at all?

The last two questions impact how this asset will be cached.

Our code changes often, so we want to include it in our bundle. This is because our bundler will give our code bundle a unique name every time it changes, based on a hash of the contents.

(We also want to process our code, but let’s ignore that for a moment.)

This means the browser will be forced to always have the latest version of our code.

3. Does it need a specific filename?

Page crawlers will be looking for a file specifically called robots.txt, so we need to preserve that filename.

For most other assets though, the filename doesn’t matter so much.

Let’s take a closer look at the differences between these two methods.

Public Directory

Any files that you put into the ~/public directory get included directly in the build output of your Nuxt app. They are not modified in any way.

This public directory is served at the root of your app. This means that if your ~/public folder looks like this:

public/
- cats.png
- songs/
  - dubstep.wav
  - chopin.mp3

You can access these files directly from your browser:

yourwebsite.com/cats.png
yourwebsite.com/songs/dubstep.wav
yourwebsite.com/songs/chopin.mp3

Remember, these files are not touched at all. The contents of ~/public are copied straight into your dist folder.

These are common things we put in the ~/public folder:

  • robots.txt
  • favicon
  • sitemaps

But generally, we’ll want to keep most things as assets.

Assets Directory

All of the code in your application is processed by a bundler. In Nuxt 3, this is Vite by default.

The bundler will start at app.vue and pull in all of its dependencies, then all of their dependencies, and so on, until all of the code in your app has been processed.

This becomes the bundled output that you can use to deploy your application.

If you import something other than “code” into a component, the bundler still has to include those files as well. Otherwise, they wouldn’t be deployed alongside all of your other code:

import screen1 from '~/assets/images/screen1.png';
import screen2 from '~/assets/images/screen2.png';
import screen3 from '~/assets/images/screen3.png';

import from '~/assets/styles/radStyleSheet.css';

When you do this, the bundler will do a few things:

  1. Process the file with any configured plugins
  2. Generate the bundled file with the file’s hash in the name (for caching purposes as we mentioned earlier)
  3. Replace the import with this new filename so your bundled code can find the file when it’s deployed

It doesn’t actually matter where you put these images or stylesheets. But we have to put all of these other assets somewhere to keep things organized, so we typically use ~/assets by convention.

One added benefit of importing assets directly like this, is that if anything is missing you will get build errors. That won’t happen if you reference something in your ~/public folder, since it isn’t processed at all.

Most things usually end up being processed by the bundler, so you’ll commonly find these in an ~/assets folder:

  • Images
  • Stylesheets
  • Icons
  • Fonts

You can learn more about managing assets from the Nuxt docs.

Michael Thiessen
Michael is a passionate full time Vue.js and Nuxt.js educator. His weekly newsletter is sent to over 11,000 Vue developers, and he has written over a hundred articles for his blog and VueSchool.

Follow MasteringNuxt on