Skip to content

Vite Plugin

The poeApp() Vite plugin handles the Poe-specific build concerns that every bundled iframe app needs. Import it from poe-apps-sdk/vite.

typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { poeApp } from "poe-apps-sdk/vite";

export default defineConfig({
  root: "app",
  build: {
    outDir: "../dist",
    emptyOutDir: true,
    rollupOptions: {
      output: {
        entryFileNames: "app-frontend.js",
        assetFileNames: "[name][extname]",
      },
    },
  },
  plugins: [react(), poeApp()],
});

The plugin handles:

  • Externals — Prevents bundling platform-provided modules (poe-apps-sdk/v1/client.js) since these are provided at runtime via import map.
  • Code splitting — Rewrites dynamic import() calls to load chunks via Poe.getBundleAssetUrl(), which returns blob URLs that work in sandboxed iframes.
  • Vendor extraction — Shared dependencies (from node_modules) are automatically extracted into a vendor chunk so they aren't duplicated across lazy chunks.
  • Backend building — Optionally builds the synced-store backend config as part of the Vite build.

The plugin does not set root, outDir, entryFileNames, or assetFileNames — consumers control their own directory structure and output naming via standard Vite config.

Backend Config Building

Pass backendEntryPoint to build the synced-store backend config automatically after the Vite build:

typescript
export default defineConfig({
  root: "app",
  build: { outDir: "../dist" },
  plugins: [
    react(),
    poeApp({ backendEntryPoint: "src/backend.ts" }),
  ],
});

The path is resolved relative to the Vite root directory. The output (synced-store-backend-config.js) is placed in the Vite output directory. This eliminates the need for a separate buildBackend() call in your build script.

Code Splitting

Dynamic import() calls are rewritten so that chunks are loaded via Poe.getBundleAssetUrl(). This is more reliable than normal browser-cached imports — the browser's HTTP cache may evict entries at any time, so lazy-loaded chunks fetched over the network can fail offline. Assets loaded via Poe.getBundleAssetUrl() are stored in IndexedDB by the top document, so they remain available even without a network connection.

No-Build vs Bundled

No-BuildBundled
Importpoe-apps-sdk/v1/client.jspoe-apps-sdk/v1/client.js
SchemaInline mutatorsdefineSchema() + defineClientConfig()
Config{ mutators, schemaVersion }Pre-built client config object
TypesNone (plain JS)Full type inference from Zod schema