App SDK overview
A Maypop app is static files running in the browser. The maypop SDK is how
it reaches everything a backend would normally provide. Maypop runs those
services; your app calls them through an authenticated session that the
Maypop host sets up for whoever is looking at the app.
| Capability | Use it for |
|---|---|
maypop.user, mode |
who is looking, and whether they can change things |
maypop.kv |
shared, live JSON data — lists, votes, scores, settings |
maypop.drive |
images, audio, video and documents |
maypop.multiplayer |
presence, rooms and real-time messages |
maypop.ai |
chat, images, video, audio and transcription |
maypop.agent |
a conversational assistant with tools over your data |
maypop.mcp |
external services linked to the app over MCP |
maypop.members() |
everyone the app reaches, and who is online |
maypop.share, link |
deep links and the share card |
maypop.notify |
notifications to the app’s audience |
There is no SQL database, server function, cron job or secret store. If your app needs one of those, keep that part on a server you host and call it from the browser.
Load the SDK
Section titled “Load the SDK”From npm, with any bundler:
npm install @basilica-digital/maypop-sdkimport { maypop } from "@basilica-digital/maypop-sdk";Or, in a plain HTML app, from Maypop:
<script src="https://api.app.maypop.ai/sdk/v1.js"></script>Both give you the same window.maypop object — use one, not both. The
TypeScript declarations ship in the package and at
https://api.app.maypop.ai/sdk/v1.d.ts. The v1 contract only grows; a
breaking change would get a new path.
Wait for ready()
Section titled “Wait for ready()”await maypop.ready();Nothing works until the host has handed the app its session. ready() is safe
to call more than once. In Studio’s preview the session is real: data you
write while building is real data.
Identity and permissions
Section titled “Identity and permissions”interface MaypopUser { id: string; username: string; role: string; avatarUrl: string | null; connected: boolean; isAnonymous: boolean; scopes: string;}
readonly user: MaypopUser | null;readonly mode: "read-write" | "read-only";readonly permissions: string[];readonly signInRequired: boolean;signIn(): void;user.idis stable for one person in one app, and different in every other app. It is not their Maypop account id. Use it for authorship and per-person keys.roleisadmin,editor,writerorreader. The app’s creator is alwaysadmin. Treat it as a hint for what to show; Maypop enforces the real permissions on every request.- Gate editing controls on
mode. People opening a link with view access, and anonymous visitors, are read-only. - Maypop owns sign-in, so don’t build a login page. When
signInRequiredis true, signing in would let this person write — show something likeSign in to saveand callmaypop.signIn(). The host handles it and reloads the app.
await maypop.ready();if (maypop.mode === "read-only" && maypop.signInRequired) { button.textContent = "Sign in to save"; button.onclick = () => maypop.signIn();}Theme and events
Section titled “Theme and events”readonly theme: "light" | "dark";setAmbientColor(rgb: [number, number, number] | null): void;on(event: "modechange" | "revoked" | "themechange", callback: () => void): () => void;The SDK mirrors the host theme onto <html data-theme> and color-scheme,
so CSS can follow it. Add data-maypop-theme="manual" to <html> to opt
out. setAmbientColor tints the glow Maypop draws behind your app.
Listen for modechange when permissions can change while the app is open
— for example, someone signs in or is given edit access. revoked means the
session ended.
Errors
Section titled “Errors”Every SDK error is an Error with a code:
| Code | Meaning |
|---|---|
maypop/read-only |
this viewer can’t write |
maypop/sign-in-required |
signing in would allow it |
maypop/forbidden |
not allowed for this viewer |
maypop/not-found |
the file or item doesn’t exist |
maypop/conflict |
a Drive file with that name already exists |
maypop/file-too-large |
over the upload limit |
maypop/ai-limit |
the viewer is out of AI credits |
maypop/ai-blocked |
a generation was refused by moderation |
maypop/not-connected |
the multiplayer peer is not connected |
maypop/unsupported |
not available here, e.g. in the local dev host |
maypop/revoked |
the session ended |
@basilica-digital/maypop-sdk/react has hooks for the common cases:
useMaypopSession, useMaypopViewer, useMaypopApp, useMaypopMode,
useMaypopTheme, and useMaypopKV(key, fallback):
import { useMaypopKV, useMaypopMode } from "@basilica-digital/maypop-sdk/react";
function Counter() { const mode = useMaypopMode(); const count = useMaypopKV("counter", 0); return ( <button disabled={mode === "read-only" || count.isMutating} onClick={() => count.setValue(count.value + 1)} > {count.value} </button> );}Deep links
Section titled “Deep links”maypop.launchPath is the path a person arrived on — from a shared link, a
notification, or a share card — and is set before ready() resolves. Apps
using a hash router get it applied automatically. See
Deep links and sharing.