File layout
miniapp.json points at the built output of each layer:
The background layer
The host evaluates your background bundle inside a per-miniapp JavaScript context and calls yourregisterMiniapp handler when the background starts. This is
where every session.* call lives.
src/background/index.ts
Startup: return your setup promise
The Mentra App keeps your miniapp’s splash screen up, and holds every message your UI sends, until the promise returned by yourregisterMiniapp handler
settles. Your session.ui.handle(...) handlers therefore exist before the UI’s
first mentra.request(...) arrives.
- Return your setup. Return or
awaitit; never discard it withvoid. Otherwise the UI can open before your handlers are registered, and its first requests fail withno handler registered. - Register UI handlers before slow work. The splash stays up while the
promise is pending. Register
session.ui.handle/session.ui.onOpenfirst, and keep network calls out of the startup path. - Failures still open the UI. If the handler throws or rejects, or takes longer than 10 seconds, the Mentra App opens the UI anyway.
src/background/index.ts
The background context is not a browser and not Node: it’s a bare JS engine
(JavaScriptCore on iOS, QuickJS on Android). There’s no
window, no DOM, and no
module resolver at runtime, which is why build.ts bundles the SDK into your
background output rather than leaving it external.Background runtime APIs
Do not infer background support from what TypeScript or Bun accepts at build time. The background bundle targets a small, cross-platform runtime. MentraOS currently provides these browser-like globals:
Common browser and Node APIs are not available in the background. This
includes
window, document, DOM elements, performance,
XMLHttpRequest, Node built-ins such as fs and path, process, and runtime
require() / dynamic module resolution. Put rendering, DOM work, and browser UI
libraries in src/ui/. Put session.* calls, hardware subscriptions, durable
state, and logic that must continue after the UI closes in src/background/.
Exchange only serializable data through mentra.* / session.ui.*.
The UI layer
The UI is a normal React app. It has zero direct native access: it reaches the glasses only by sending messages to its own background layer. It mounts inside<MentraProvider> and must call mentra.ready() on boot so the host
knows it’s mounted.
src/ui/main.tsx
Talking between layers
The two layers never share memory: they pass messages over a typed bus. You declare every channel once insrc/shared/channels.ts; both halves import it, so
names and payload shapes are enforced at compile time.
src/shared/channels.ts
mentra global:
session.ui:
mentra.send from the UI buffers
until the WebView calls ready(), while session.ui.send from the background
drops silently when no WebView is open (there’s no one to receive it).
There’s no runtime channel registry: channel names are opaque strings on the
wire, validated only by TypeScript. See
Interop & Actions for the separate,
cross-miniapp RPC layer (session.actions).
Lifecycle
- Install: the host unzips the bundle, validates the manifest, spawns the
background JS context, and runs your
registerMiniapphandler. Background is alive. - UI opens: the user taps your tile; the host spawns a WebView, injects the
mentrashim, and loadsui/index.html. The WebView callsmentra.ready(). Once your background is ready (see Startup), the splash hides andsession.ui.onOpenfires in the background. - UI closes: the user navigates away; the host destroys the WebView. The
background context stays alive and
session.ui.onClosefires. - Disable / uninstall: the host tears down your subscriptions and kills the background context.
Crash recovery
If your background layer crashes, the host restarts it automatically. Repeated crashes back off and eventually show the user a “try again” prompt, so aim to reach a stable state quickly on startup. A UI request that was in progress when the background crashed fails withBACKGROUND_RESTARTED and is never replayed;
retry it yourself if it is safe to repeat.
Next steps
The manifest
Declare entries, permissions, and hardware in
miniapp.json.The session
The background-side handle to every glasses capability.
The UI layer
React hooks, the capsule menu, and safe areas.
Interop & Actions
Let other miniapps (and Mentra AI) call yours.

