Static Asset Handling
- Related: Public Base Path
- Related:
assetsInclude
config option
Importing Asset as URL
Importing a static asset will return the resolved public URL when it is served:
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
For example, imgUrl
will be /img.png
during development, and become /assets/img.2d8efhg.png
in the production build.
The behavior is similar to webpack's file-loader
. The difference is that the import can be either using absolute public paths (based on project root during dev) or relative paths.
url()
references in CSS are handled the same way.If using the Vue plugin, asset references in Vue SFC templates are automatically converted into imports.
Common image, media, and font filetypes are detected as assets automatically. You can extend the internal list using the
assetsInclude
option.Referenced assets are included as part of the build assets graph, will get hashed file names, and can be processed by plugins for optimization.
Assets smaller in bytes than the
assetsInlineLimit
option will be inlined as base64 data URLs.
Explicit URL Imports
Assets that are not included in the internal list or in assetsInclude
, can be explicitly imported as an URL using the ?url
suffix. This is useful, for example, to import Houdini Paint Worklets.
import workletURL from 'extra-scalloped-border/worklet.js?url'
CSS.paintWorklet.addModule(workletURL)
Importing Asset as String
Assets can be imported as strings using the ?raw
suffix.
import shaderString from './shader.glsl?raw'
Importing Script as a Worker
Scripts can be imported as web workers with the ?worker
or ?sharedworker
suffix.
// Separate chunk in the production build
import Worker from './shader.js?worker'
const worker = new Worker()
// sharedworker
import SharedWorker from './shader.js?sharedworker'
const sharedWorker = new SharedWorker()
// Inlined as base64 strings
import InlineWorker from './shader.js?worker&inline'
Check out the Web Worker section for more details.
The public
Directory
If you have assets that are:
- Never referenced in source code (e.g.
robots.txt
) - Must retain the exact same file name (without hashing)
- ...or you simply don't want to have to import an asset first just to get its URL
Then you can place the asset in a special public
directory under your project root. Assets in this directory will be served at root path /
during dev, and copied to the root of the dist directory as-is.
The directory defaults to <root>/public
, but can be configured via the publicDir
option.
Note that:
- You should always reference
public
assets using root absolute path - for example,public/icon.png
should be referenced in source code as/icon.png
. - Assets in
public
cannot be imported from JavaScript.
new URL(url, import.meta.url)
import.
const imgUrl = new URL('./img.png', import.meta.url).href
document.getElementById('hero-img').src = imgUrl
This works natively in modern browsers - in fact, Vite doesn't need to process this code at all during development!
This pattern also supports dynamic URLs via template literals:
function getImageUrl(name) {
return new URL(`./dir/${name}.png`, import.meta.url).href
}
During the production build, Vite will perform necessary transforms so that the URLs still point to the correct location even after bundling and asset hashing. However, the URL string must be static so it can be analyzed, otherwise the code will be left as is, which can cause runtime errors if build.target
does not support import.
// Vite will not transform this
const imgUrl = new URL(imagePath, import.meta.url).href
Does not work with SSR
This pattern does not work if you are using Vite for Server-Side Rendering, because import.
have different semantics in browsers vs. Node.js. The server bundle also cannot determine the client host URL ahead of time.
target
needs to be es2020
or higher
This pattern will not work if build-target or optimizedeps.esbuildoptions.target is set to a value lower than es2020
.