Appearance
Assets
Three ways to ship + load files in a Poe app.
Static asset → import "./img.svg?url"
Vite inlines as data: URL when under assetsInlineLimit. Use for known paths.
typescript
// @ts-expect-error — no built-in TS types for Vite URL imports
import logoUrl from "./assets/logo.svg?url";
img.src = logoUrl;typescript
// vite.config.ts — bump limit so files inline (sandbox iframe origin is "null",
// hashed asset paths don't reliably resolve).
export default defineConfig({
build: { assetsInlineLimit: 10 * 1024 * 1024 }, // 10 MB
plugins: [poeApp()],
});?raw for inline text.
Runtime asset → Poe.getBundleAssetUrl(path)
Returns cached blob: URL. Use for dynamic paths, large files, no-build apps.
typescript
img.src = await Poe.getBundleAssetUrl("/assets/hero.png");
const data = await fetch(await Poe.getBundleAssetUrl("data/levels.json")).then(r => r.json());Leading slash / bare / ./ all equivalent. Cached. See client-api.md.
Lazy code chunk → import("./mod")
Plugin rewrites dynamic import() to fetch chunks via getBundleAssetUrl(). Lazy CSS auto-loaded with its JS chunk.
typescript
const Heavy = React.lazy(() => import("./Heavy")); // React
const Heavy = lazy(() => import("./Heavy")); // SolidJS
const { compute } = await import("./compute"); // plainPick
| Need | Use |
|---|---|
| Static path, small | import "...?url" |
| Dynamic path / large / no build | Poe.getBundleAssetUrl(path) |
| Defer code module | import("./mod") |