Static Assets
Vantris handles images, fonts, media, and other files as first-class assets — importing one resolves it to a (hashed, in the build) URL, and the behaviour is identical in dev and build.
Importing an asset
Import a file from JavaScript and you get its URL:
import logoUrl from "./logo.svg";
const img = document.createElement("img");
img.src = logoUrl; // "/src/logo.svg" in dev, "/assets/logo-a1b2c3.svg" in buildIn a build, the file is copied to outDir with a content hash, and the import resolves to an absolute URL derived from base. In dev, it resolves to the served path. Either way, your code is the same.
Supported asset types
| Category | Extensions | Import resolves to |
|---|---|---|
| Images | png, jpg, jpeg, gif, svg, webp, avif, ico, bmp | URL |
| Fonts | woff, woff2, ttf, otf, eot | URL |
| Media | mp4, webm, mp3, wav, … (audio/video) | URL |
| Other | wasm, txt | URL |
| Data | json | parsed data |
JSON
json is special — it's imported as data, not a URL:
import config from "./config.json";
console.log(config.version);
// Named imports work too (fields are individually exported):
import { version } from "./config.json";WebAssembly & text
wasm and txt resolve to a URL, so you fetch/instantiate them yourself:
import wasmUrl from "./add.wasm";
const { instance } = await WebAssembly.instantiateStreaming(fetch(wasmUrl));
import shaderUrl from "./shader.txt";
const source = await fetch(shaderUrl).then((r) => r.text());Assets in CSS and HTML
- CSS —
url("./bg.png")is rewritten and hashed. See CSS. - HTML —
src/hrefin<link>/<img>/<script>that point intorootDirare hashed and rewritten. Public and external references are left untouched. See Building → HTML processing.
The public/ directory
Files in publicDir (default ./public) are the escape hatch for assets you don't import — files that need a stable, predictable URL:
- Served at the root during dev:
public/favicon.svg→/favicon.svg. - Copied verbatim into
outDiron build — never hashed, never transformed.
Reference them with an absolute path from the root, not a relative import:
<link rel="icon" href="/favicon.svg" />// public assets are referenced by URL, not imported
const iconUrl = "/favicon.svg";Use public/ for: favicon, robots.txt, manifest.webmanifest, files referenced by a third party, or anything whose URL must not change between builds. Everything else — assets you import — should live under rootDir so they're hashed and cache-busted.
public/ vs. imported assets
Imported (import x from "./x.png") | public/x.png | |
|---|---|---|
| Location | anywhere under rootDir | public/ only |
| Hashed in build | ✅ | ❌ (verbatim) |
| Referenced by | the import's return value | a fixed absolute URL |
| Cache-busting | automatic | none — you manage it |
| Use for | app assets | favicons, robots.txt, fixed URLs |
The base path
Imported asset URLs are prefixed with base in the build, so they resolve correctly when the app is deployed under a sub-path or served from a CDN. Public assets, referenced by absolute path, resolve against the same base at serve time. See Building → base.
Type declarations
vantris/client declares these asset modules so TypeScript types the imports (e.g. import logo from "./logo.svg" as string). Add it to your tsconfig types. See Client Types.