Skip to content

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");        // plain

Pick

NeedUse
Static path, smallimport "...?url"
Dynamic path / large / no buildPoe.getBundleAssetUrl(path)
Defer code moduleimport("./mod")