Open Form

Getting started

Scaffold a workspace, have your coding agent write a form, preview it in Studio and collect your first response in a few minutes.

Open Form needs Node.js 22.13 or later. It stores responses with Node's built-in node:sqlite, so there is nothing else to install.

Install

npm create open-form@latest my-forms
cd my-forms
npm run dev

The scaffolder creates a workspace (abridged — see open-form init for the full tree):

my-forms/
├── forms/                  # one folder per form, with its own theme/ once it has one
│   └── contact/index.tsx   # a starter form
├── themes/                 # reference themes for new forms (just a README at first)
├── .agents/skills/         # bundled agent skills
├── open-form.config.ts     # project config, with your time zone
├── AGENTS.md               # agent rules (CLAUDE.md links to it)
└── package.json

The dev script starts the dev server with Studio at http://localhost:3000/studio. Every form is live at /f/<id> and hot reloads as its file changes.

Forms import from @open-form-build/core, the package the workspace depends on, and its CLI is open-form.

Commands in these docs come in npm, pnpm, yarn and bun tabs, matching the ones in your workspace's README; pick yours once and every page follows. In running text, open-form <command> means the CLI run through your package manager: npx open-form, pnpm exec open-form, yarn open-form or bunx open-form.

Author with an agent

Open the workspace in any agent-aware editor — Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf, Zed. From the agent prompt:

/create-form registration for a developer meetup. Ask for name, email, role
and experience. If they're a student, ask for their school. 100 seats.

The skill asks what it needs, proposes three visual directions for your audience, then writes forms/<id>/index.tsx and checks it with open-form check. See create-form.

Prefer to write it yourself? Start from a stub:

npx open-form create meetup

Preview and iterate

Open the form in Studio (/studio/<id>):

  1. Preview it at desktop, tablet and mobile widths. The step buttons jump to any page, including conditional ones; When closed shows each closed screen.
  2. Switch to Edit, select any element and change its text in place, or leave a comment for your agent.
  3. Run /apply-comments in your agent. It applies each note in the source and removes the marker.

See Studio and Edit mode & comments.

Give it a look

Each form owns its look in forms/<id>/theme/, imported by the form and passed to <Form theme>:

forms/meetup/index.tsx
import theme from './theme/index.ts';

export default function Meetup() {
  return <Form theme={theme}>…</Form>;
}

A form from open-form create starts with an empty theme/index.ts, the plain base look. Start one from a starter's tokens instead with --theme:

npx open-form create meetup --theme terminal

The starters are classic, editorial, terminal, brutalist and conversational. They're references: /create-form reads the chosen one and writes the rest of the form's look, taking only what that form uses. The look is the form's to change, by hand, through your agent, or with Adjust in Studio's preview, and only that form changes with it. For a look of your own, ask your agent for one with /create-theme: it lands in themes/ as a reference for new forms. See Themes.

Collect responses

Submissions from the preview are real: they are stored in .open-form/open-form.db (or Postgres, if you set it up) and appear under the form's Responses tab, where you can search, open, delete and export them.

npx open-form responses export meetup -o responses.xlsx

Build and deploy

npm run build
OPEN_FORM_ADMIN_PASSWORD='a long passphrase you choose' npm start

open-form start serves the forms, the API and SQLite from one Node process. With SQLite it needs a persistent disk for .open-form/ — a VPS, Docker, or Railway / Fly.io with a volume; with Postgres it doesn't. Studio is only available in production when OPEN_FORM_ADMIN_PASSWORD is set, or read-only for everyone with studio.public. See Production, deploy the whole project to Vercel with Postgres, or mount the API inside an existing app with Next.js.

What's next

On this page