> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chatblocks.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Canvas and blocks

> The spatial model — canvases hold placements, placements pin blocks at positions, blocks render in sandboxed iframes.

Chatblocks has three primitives. Everything else is a refinement of one of them.

## Canvas

An infinite spatial board with a dot-grid background. A canvas holds **placements** — `{ blockId, x, y, w, h, version }` rows that anchor a block to a position. Canvases are workspace-owned.

You can have as many canvases as your plan allows (Free: 2, Builder: 20, Enterprise: 1000). Each canvas has its own visibility setting.

### Visibility

Three states, set from the **Sharing** tab in the canvas Manage panel (`?manage=1&tab=sharing`):

* **`private`** — only workspace members can open it. The default for new canvases.
* **`workspace`** — same access as `private` today; reserved for future role-based sharing.
* **`public`** — anyone can view it without logging in, at the stable pretty URL `chatblocks.ai/<username>/<slug>`. Public is the only shareable tier. Public canvases are search-indexable unless you tick the per-canvas **Hide from search engines** checkbox (`seoNoindex`), which drops the page from the sitemap and sets `noindex` on its robots meta. Transitioning back to `private` or `workspace` makes the public URL stop resolving.

There's no token-based share link — sharing is just "make it public," and the URL is the pretty `/<username>/<slug>` path (no opaque `/c/<token>` URLs).

**Per-block sharing.** Every block has a "Copy link" (in the tile menu and the expanded view's Share button) that yields a public deep link straight to that block: `chatblocks.ai/<username>/<slug>?expanded=<placementId>`. If the canvas isn't public yet, a manager is prompted to make it public before the link is copied.

The featured homepage canvas at `chatblocks.ai/` is a separate concept — site admins flip `isFeatured` on a `public` canvas to surface it as the landing page.

## Block

An independent React or HTML app, rendered in a sandboxed iframe via `/api/blocks/:id/render`. Blocks own their visuals end-to-end — the platform provides the sandbox, not a design system. Think "hawker market stall," not "window in an OS."

A block has metadata (name, description, tags), a **manifest** (widget config, data bindings), and one or more **published versions**. Canvas placements pin to a specific `publishedVersionId`, so updating a block doesn't silently change what's on the canvas — you re-publish to push a new version.

### Render formats

The manifest's `format` field is one of two values:

<Tabs>
  <Tab title="code">
    The default. A virtual filesystem of source files authored in the in-app Monaco editor or by your agent via MCP `blocks.setFiles`. On `blocks.build`, Vercel Sandbox compiles the source to a static artifact tree (HTML + JS chunks + CSS + assets), uploads it to Convex storage, and pins it to a `blockPublishedVersion`. The render route serves the published artifact from storage.

    Use this for anything beyond a fixed page — JSX, hooks, data bindings, animations, anything that runs JavaScript.
  </Tab>

  <Tab title="embed">
    The block points at an external URL (the manifest's `embed.url`). The render route synthesizes a platform-owned iframe shell with sandbox attributes the creator can't override — the platform always controls how the embedded site is sandboxed. No build pipeline, no artifact storage.

    Use this for blocks that wrap an existing site you own or that's safe to frame.
  </Tab>
</Tabs>

A block is "renderable" — shows as an iframe rather than a placeholder tile — when it has a published artifact (for `code`) or a valid `embed.url` (for `embed`). The canvas placement layer (`canvas-host.tsx`) computes this and the render route's branching has to stay in sync with it.

## Placement

A `blockPlacements` row: `{ canvasId, blockId, x, y, w, h, publishedVersionId }`. A block can appear on multiple canvases, but **at most one placement per block per canvas** — `placeBlock` rejects duplicates.

Placements are created via:

* **The in-app submission flow** — a workspace member submits a block to a canvas; the canvas curator (workspace admin) approves; approved blocks auto-place.
* **MCP `placements.create`** — your agent places a block directly.
* **Direct placement on your own canvas** — no approval step needed when you own both sides.

## Authoring loop

The full MCP authoring loop your agent drives:

<Steps>
  <Step title="blocks.init">
    Scaffold a new block. Returns a `blockId`. By default the block is unpublished and not placed anywhere.
  </Step>

  <Step title="blocks.setFiles">
    Write source files. The block holds a virtual filesystem with `src/App.tsx` as the entry point for `code`-format blocks.
  </Step>

  <Step title="blocks.build">
    Run the Vercel Sandbox build. Produces an HTML bundle and uploads it to Convex storage. Counts against your monthly build quota.
  </Step>

  <Step title="blocks.publish">
    Cut a new published version. The render route now serves the bundle. Canvas placements that pin to "latest" pick it up immediately; placements pinned to an older version are unaffected.
  </Step>
</Steps>

Full reference: [Authoring → Via an agent](/authoring/via-agent).

## What's next

<CardGroup cols={2}>
  <Card title="Manifest" icon="file-code" href="/authoring/manifest">
    The full schema for a block's manifest — widget config, data binding, projection mapping.
  </Card>

  <Card title="Runtime" icon="cube" href="/authoring/runtime">
    What's available inside the iframe — render env, data injection, security boundaries.
  </Card>
</CardGroup>
