Skip to main content
Before you start building, it helps to understand how your app fits into the webAI platform. The model is simple: your app is a web page, the platform runs it in a sandbox, and it injects APIs for you to use.

The single-file model

A webAI app is a single, self-contained HTML file. All JavaScript, CSS, images, and other assets must be inlined or bundled into that one file. There are no external asset dependencies at runtime — your app needs to work without fetching anything from a server. This constraint exists because apps are distributed . When someone shares an app in a space, the entire app is transferred as a single payload. There’s no server to host static assets.
If you’re building with a framework like React or Vue, you can still use your normal development workflow. A bundler like Vite produces the single-file output at build time. You develop normally and only bundle when you’re ready to upload.

How apps run

When you upload an app, the webAI shell:
  1. Stores the HTML content in the browser’s localStorage under the key apogee-uploaded-apps
  2. Registers it in the launcher alongside the built-in apps
  3. When launched, loads your HTML into a sandboxed <iframe>
  4. Injects platform globals (OasisHost, CollaborationManager, etc.) into the iframe’s window before your code executes
Your app runs entirely client-side. There is no server-side runtime — everything happens in the browser.

Constraints

ConstraintDetails
Single fileAll JS, CSS, and assets must be inlined into one HTML file
Client-side onlyNo server-side code. Your app runs in the browser
No external fetchesDon’t depend on CDN-hosted libraries or external APIs for core functionality
Iframe sandboxYour app runs in an iframe — some browser APIs may be restricted
Graceful degradationShell APIs are null outside of webAI (e.g., during local development). Always check before calling

Development vs. production

During development, your app runs outside the webAI shell — in a normal browser tab via a dev server. This means all shell APIs (OasisHost, CollaborationManager, etc.) will be null. Your app should handle this gracefully and still be functional for UI development and testing. When your app runs inside the webAI shell, those globals are injected and available.
const host = window.OasisHost ?? window.parent?.OasisHost;
if (host) {
  // Running inside webAI — full API access
} else {
  // Local development — show a fallback or mock data
}
A good pattern is to show a subtle banner in dev mode: “Running outside webAI — AI and collaboration features unavailable.” This way you can still iterate on your UI without needing the full shell running.
While you can build an app in a single hand-written HTML file, most developers use a framework with a bundler. Here’s a typical project layout:
my-app/
├── src/
│   ├── App.jsx          # Your app's root component
│   ├── webai.js         # Shell API helpers (safe accessors, AI streaming)
│   └── ...              # Other components and logic
├── scripts/
│   └── upload.js        # Generates the upload script for the shell
├── vite.config.js       # Configured with vite-plugin-singlefile
├── package.json
└── dist/
    └── index.html       # Build output — the single file you upload
The key pieces:
  • src/webai.js is a thin helper module that wraps access to all shell-injected globals with null safety. This is your integration layer — it keeps shell-specific code out of your components.
  • vite-plugin-singlefile tells Vite to inline all assets into a single index.html at build time, satisfying the single-file requirement.
  • scripts/upload.js generates a browser console script that uploads your built HTML into the webAI shell’s localStorage.

Frameworks

webAI apps work with any framework — or no framework at all. The most common choices are:
Use Vite with the React template. React’s component model maps naturally to the API patterns (polling AI status with useEffect, managing space state with useState).
npm create vite@latest my-app -- --template react
cd my-app && npm install
npm install --save-dev vite-plugin-singlefile

Theme integration

The webAI shell sends theme updates to your app via postMessage. This lets your app respond to light/dark mode changes in real time.

Listening for theme changes

window.addEventListener('message', (e) => {
  if (e.data?.type === 'canvas:theme') {
    const theme = e.data.theme; // 'light' or 'dark'
    document.documentElement.setAttribute('data-theme', theme);
    // Update your CSS variables or class names here
  }
});

Other shell messages

The shell communicates several event types to your app’s iframe:
Message typeDescription
canvas:themeTheme changed (light/dark). Payload: { theme: 'light' | 'dark' }
canvas:connectedYour app is now connected to a collaboration space
canvas:disconnectedYour app disconnected from the space
canvas:usersThe user list in the space changed
canvas:user-leftA user left the space
The built-in sub-apps use a shared theme module that handles all of this automatically. If you’re building a vanilla HTML app, the canvas:theme listener above is all you need.

Next steps

Accessing shell APIs

Learn the universal pattern for safely accessing all platform APIs from your app code.