llms.txt

Overview

Overview

A Farcaster Snap is an interactive embed inside a cast. It renders as a card in the feed and can be multi-page, stateful, and dynamic. Snaps are defined by a JSON response served by an external server. The Farcaster client renders the JSON — it never executes arbitrary code.

Snaps are the evolution of Frames: richer components, multi-page flows, dynamic content, and the same server-driven model.

Example interaction:

  1. A cast embed points to a URL that implements the snap protocol
  2. The client GETs that URL, signaling snap support. The server responds with a JSON SnapResponse
  3. The client renders the ui tree using the component catalog
  4. The user interacts with field components (input, slider, switch, toggle_group) — values are stored locally
  5. The user taps a button element whose on.press is bound to a submit action
  6. The client collects all field values and POSTs a signed payload to the target URL
  7. The server returns a new SnapResponse — the client renders it as the next page
  8. Repeat

Content Negotiation

The snap media type is application/vnd.farcaster.snap+json. Clients and servers use HTTP headers (Accept, Content-Type, Vary, and Link) to signal Snap support and so the same URL can serve snap JSON or fallback content. See HTTP Headers for details.

Authentication

Main page: Authentication

Snap POST requests use JSON Farcaster Signatures (JFS) for authentication.

Response Structure

Valid snap responses have roughly this shape:

{
  "version": "1.0",
  "theme": { "accent": "purple" },
  "effects": ["confetti"],
  "ui": {
    "root": "page",
    "elements": {
      "page": {
        "type": "stack",
        "props": {},
        "children": ["header", "guess", "submit"]
      },
      "header": {
        "type": "item",
        "props": { "title": "Daily Wordle", "description": "Attempt 3 of 6" }
      },
      "guess": {
        "type": "input",
        "props": { "name": "word", "label": "Your guess", "maxLength": 5 }
      },
      "submit": {
        "type": "button",
        "props": { "label": "Submit", "variant": "primary" },
        "on": {
          "press": {
            "action": "submit",
            "params": { "target": "https://wordle.example.com/guess" }
          }
        }
      }
    }
  }
}

Top-Level Fields

FieldTypeRequiredDefaultDescription
version"1.0"YesSpec version
themeobjectNo{ accent: "purple" }Theme configuration
theme.accentPaletteColorNo"purple"Accent color for buttons, progress bars, etc.
effectsstring[]NoVisual effects applied on render. See Effects
uijson-render SpecYesThe UI tree

The ui Field

The ui field is a json-render Spec — a flat element map with typed components, props, and event bindings.

FieldTypeRequiredDescription
ui.rootstringYesID of the root element
ui.elementsRecord<string, UIElement>YesFlat map of all elements by ID
ui.stateRecord<string, unknown>NoInitial state for the json-render state store

Element Structure

Every element in ui.elements follows this shape:

FieldTypeRequiredDescription
typestringYesComponent name (see Elements)
propsobjectYesComponent-specific properties (use {} if none)
childrenstring[]NoChild element IDs (for containers and action slots)
onobjectNoEvent bindings — on.press triggers an action when a button is tapped

POST Payload

When a submit action fires, the client sends a JFS-signed envelope containing:

FieldTypeDescription
fidnumberFarcaster user ID
inputsRecord<string, value>Field values keyed by component name prop
button_indexnumberButton index (0-based)
timestampnumberUnix timestamp in seconds

Input values by field type:

ComponentValue sent
inputstring
slidernumber
switchboolean
toggle_group (single)string
toggle_group (multiple)string[]

Broken Snaps

If the snap URL is unreachable, returns invalid JSON, or fails schema validation:

  • The embed does not render in the feed
  • The cast displays normally with the snap URL shown as plain text in the cast body
  • The client may cache the last valid first page and show it with a "stale" indicator, at its discretion

If a submit action fails (timeout, server error, or invalid JSON response):

  • The client stays on the current page — it is never replaced with a blank screen or error page
  • An inline error is shown on the current page: "Something went wrong. Tap to retry."
  • The user can retry the same button tap, or close/navigate away from the snap

There is no client-managed back button. Navigation is server-driven.

If a snap wants "go back" functionality, it includes a button with a submit action that POSTs to the server, and the server returns the appropriate previous page. The server is responsible for maintaining navigation state.

Versioning

The version field is required on every response. Clients must check this field.

  • If the version is supported, render normally
  • If the version is newer than the client supports, show a fallback: the snap name/URL with a message "Update Farcaster to view this snap"
  • Snaps should target the lowest version that supports their component types

Validator (@farcaster/snap)

Runtime validation lives in @farcaster/snap (pkgs/snap). The package validates snap JSON against the schema.

pnpm --filter @farcaster/snap test

Hono-oriented HTTP wiring (registerSnapHandler) is in @farcaster/snap-hono.