Getting started
Scaffold a workspace 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 devThe 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.jsonThe 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.
The rest of these docs use pnpm: pnpm dev, pnpm exec open-form <command>.
With npm, yarn or bun, run the CLI as npx 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:
pnpm exec open-form create meetupPreview and iterate
Open the form in Studio (/studio/<id>):
- Preview it at desktop, tablet and mobile widths. The step buttons jump to any page, including conditional ones; When closed shows each closed screen.
- Switch to Edit, select any element and change its text in place, or leave a comment for your agent.
- Run
/apply-commentsin 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 keeps its own copy of its look in forms/<id>/theme/. A form from
/create-form already has one. For the stub, copy a starter into the form:
pnpm exec open-form theme copy terminal meetupThat writes forms/meetup/theme/. Then apply it in the form, importing it above
any CSS of the form's own:
import theme from './theme/index.tsx';
export default function Meetup() {
return <Form theme={theme}>…</Form>;
}pnpm exec open-form create meetup --theme terminal does both for a new form.
The starters are classic, editorial, terminal, brutalist and
conversational. The copy is the form's to change, 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 and appear under the form's Responses tab, where
you can search, open, delete and export them.
pnpm exec open-form responses export meetup -o responses.xlsxBuild and deploy
pnpm build
OPEN_FORM_ADMIN_PASSWORD='a long passphrase you choose' pnpm startopen-form start serves the forms, the API and SQLite from one Node process.
It needs a persistent disk for .open-form/ — a VPS, Docker, or Railway /
Fly.io with a volume. Studio is only available in production when
OPEN_FORM_ADMIN_PASSWORD is set. See Production,
or mount the API inside an existing app with Next.js.