Every block renders inside a sandboxed iframe viaDocumentation Index
Fetch the complete documentation index at: https://docs.chatblocks.ai/llms.txt
Use this file to discover all available pages before exploring further.
/api/blocks/[blockId]/render. The browser hits that route, the route picks a render path based on the block’s format, and the iframe loads. This page covers what happens between request and pixel.
Two render paths
The render route dispatches onmanifest.format:
- format: code
- format: embed
Published via the in-app editor or by your agent over MCP. Your file set (entry + dependencies) is run through Vercel Sandbox, which produces a single HTML bundle. That bundle is uploaded to Convex storage and the storage id is written to
blockPublishedVersions.bundleStorageId.On render, the route fetches the bundle from Convex storage and streams it as the iframe’s text/html body. Strict CSP, no cross-origin framing, no inline scripts beyond the bundled ones.The isRenderable predicate
A block tile on a canvas is either an iframe or a placeholder. The predicate that decides which lives incanvas-host.tsx → toNodes:
Bundle storage
Forcode blocks, the blockPublishedVersions row carries:
bundleStorageId— Convex storage id pointing at the compiled HTML bundle.publishedAt— when this version was cut.manifest— frozen snapshot ofchatblocks.jsonat publish time.
publishedVersionId, so updating a block doesn’t silently change what’s on the canvas. To roll forward, re-pin (or set the placement to “latest”).
Iframe sandbox
The render iframe is sandboxed at the outer layer. The platform sets the sandbox attributes; block authors don’t (and can’t) influence them. Defaults:codeblocks: scripts allowed, same-origin denied, top navigation denied, popups denied, forms allowed only when theformspermission is declared in the manifest.embedblocks: same baseline, with the additional inner-iframe profile (scripts-onlyorstrict) applied to the embedded site.
connect-src in the CSP is restricted to the origins listed in manifest.network.allowedOrigins. If your block needs to call an API, declare the network permission and list the API origin.
Data injection
Blocks with abinding get live data on a refresh cadence. The aggregator runs the query, applies the projection, and writes the result into block.manifest.widget.data.
On render, the route serializes the current widget.data and injects it into the iframe via a known global (the exact mechanism is an implementation detail — agents and authors should treat widget.data as the contract). Refreshes happen out-of-band; the iframe re-renders when its container observes a change.
For agents pushing one-shot data without a connector — for example, a scheduled-run agent that computes a value and stuffs it into the widget — there’s the dedicated blocks.setWidgetData MCP tool. It writes directly to widget.data without changing source files. See MCP tool reference.
Versioning behavior
A new published version supersedes the old one only for placements pinned to “latest.” Placements pinned to a specific version are stable until you change them. The render route uses thepublishedVersionId carried on the placement (or the latest version if rendering outside a placement context — e.g. the editor preview, or a direct block-detail link).
Re-publishing the same source produces a new blockPublishedVersions row with a new bundleStorageId even if the build output is byte-identical — versions are append-only, not deduplicated.
What the iframe can’t do
The sandbox is the trust boundary. A few things the iframe explicitly cannot do, regardless of what the source code tries:- Read cookies from the parent origin.
same-originis denied; the iframe has its own opaque origin. - Reach the platform’s internal APIs. Convex endpoints are not in any block’s CSP allowlist.
- Navigate the top frame.
allow-top-navigationis not set. - Pop up windows.
allow-popupsis not set. - Persist anything across reloads unless the
storage.kvpermission is declared, in which case the platform exposes a key/value API scoped to the viewer.
permissions array — most “missing capability” issues are unset opt-ins, not platform bugs.
What’s next
Manifest
Every field that controls what the runtime allows.
Widget
The iOS widget projection that rides alongside the runtime render.