create-form-builder
Skill1 starsChanged 27 days agoMITView on GitHub
Scaffold and use the Buildpad Dynamic Form Builder — a visual drag-and-drop authoring UI (FormBuilder) plus a runtime renderer (DynamicForm) layered on CollectionForm/VForm. An admin arranges a collection's fields into sections, sets width/required/readonly/hidden, and authors conditional logic, saving a reusable FormDefinition into an fb_definitions collection (the schema is never mutated). Use when the user says form builder, build a form, dynamic form, form designer, visual form editor, conditional form, or wants end users to design forms.
- Reads credentials
In this repository66 other files it gives its agents.
- add-ai-chatSkill
- add-buildpadSkill
- add-commentsSkill
- add-cronSkill
- add-external-oauthSkill
- add-filesSkill
- add-i18nSkill
- add-mcp-serverSkill
- add-microappSkill
- add-microfrontendSkill
- add-microfrontend-zonesSkill
- add-mobile-appSkill
- add-multitenancySkill
- add-usersSkill
- add-workflowsSkill
- amplify-env-varsSkill
- authentication-proxySkill
- buildpad-debugSkill
- buildpad-discoverySkill
- buildpad-implSkill
- buildpad-referenceSkill
- buildpad-reviewSkill
- buildpad-spec-batchSkill
- buildpad-spec-designSkill
- buildpad-spec-initSkill
- buildpad-spec-quickSkill
- buildpad-spec-requirementsSkill
- buildpad-spec-statusSkill
- buildpad-spec-tasksSkill
- buildpad-steering-customSkill
- buildpad-steeringSkill
- buildpad-validate-designSkill
- buildpad-validate-gapSkill
- buildpad-validate-implSkill
- buildpad-verify-completionSkill
- code-simplificationSkill
- context-engineeringSkill
- create-api-routeSkill
- create-collectionSkill
- create-componentSkill
- create-cronSkill
- create-featureSkill
- create-migrationSkill
- create-projectSkill
- create-rbacSkill
- create-serviceSkill
- create-testsSkill
- create-workflowSkill
- daas-platformSkill
- debugging-and-error-recoverySkill
- generate-docsSkill
- git-workflow-and-versioningSkill
- grant-module-accessSkill
- hooks-extensionsSkill
- idea-refineSkill
- incremental-implementationSkill
- manage-scopeSkill
- migrate-to-engineSkill
- performance-optimizationSkill
- planning-and-task-breakdownSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Read .buildpad/project.json at the project root.
- Very specific: line 19
If a folder named supabase at the project root holds a file named config.toml, or holds a folder named migrations that holds a file whose …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Create with the Dynamic Form Builder
<!-- backend-gate:begin v1 -->Establish the backend first. A Buildpad project's backend is either the Buildpad Engine or Supabase, and some parts of this file hold for one of them only. Before you act on anything in this file, find the backend with these steps, in order, and stop at the first step that answers:
- Read
.buildpad/project.jsonat the project root. If it is JSON and its fieldbackendis"engine"or"supabase", that is the backend. If.buildpad/backend-answer.jsonnames the other backend, tell the developer the two files disagree, and follow.buildpad/project.json. - Otherwise read
.buildpad/backend-answer.jsonin the same way; if itsbackendis"engine"or"supabase", that is the backend. - If a folder named
supabaseat the project root holds a file namedconfig.toml, or holds a folder namedmigrationsthat holds a file whose name ends in.sql, the backend is Supabase. - Otherwise ask the developer once: "Is this project's backend the Buildpad Engine or
Supabase?" Write the answer to
.buildpad/backend-answer.jsonas{"buildpadBackendAnswer": 1, "backend": "engine"}or{"buildpadBackendAnswer": 1, "backend": "supabase"}, and ask the developer to commit that file. Do not add it to.gitignore. Never write.buildpad/project.json.
To find the backend, do not open an environment file (.env, .env.*) or an MCP configuration
file, and do not call a tool that returns project credentials.
Then follow only the parts for that backend. A part starts at a line that holds only a comment
whose text is buildpad + -backend: engine or buildpad + -backend: supabase, and ends at the
next line that holds only a comment whose text is /buildpad + -backend. A comment that merely
mentions a backend is not a part. A file whose first line, after any front matter, holds only a
comment whose text is buildpad + -backend-file: engine (or supabase) holds for that backend
only, and so does every file in a folder named engine-only or supabase-only. Skip the other
backend's parts and copy no code from them; everything else holds for both. On an Engine project, when this file says that
something is not available, tell the developer so and stop: do not build a substitute.
<!-- backend-gate:end --><!-- buildpad-backend: engine -->On an Engine project with Customer_Tenants each customer of the application is a tenant of its own, and the project's tenant is the Blueprint every customer is copied from. Establish the tenancy mode as the add-multitenancy skill says: from the field tenancy of .buildpad/project.json, else from .buildpad/tenancy-answer.json. In the mode customer-tenants that skill's guide engine-customer-tenants replaces what ES-3, ES-4, ES-5, ES-7 and ES-9 say about the project's tenant; in the mode scope they hold as written.
<!-- /buildpad-backend -->A ready-made /forms experience: a visual form builder plus a runtime renderer on top of the schema-driven CollectionForm/VForm runtime. An admin arranges a collection's fields into ordered sections, sets width / required / readonly / hidden, and authors conditional logic, then saves the result as a reusable, named form definition. Definitions are stored as items in an ordinary collection and merged onto the live schema at render time via buildFieldsFromDefinition — the schema is never mutated.
This is a two-step workflow: author a definition with FormBuilder, then fill it with DynamicForm. That is why this is a create-* workflow skill, not just a component install.
Relationship to VForm (do not confuse)
@buildpad/ui-forms(this — plural) = the Form Builder module.@buildpad/ui-form(singular) = the low-levelVFormruntime the builder wraps.
The builder emits fields consumed by CollectionForm → VForm. Do not re-implement VForm's controlled-component pattern here — the runtime handles state and submission. See vform-usage for the substrate.
Prerequisite: the definitions collection
Definitions are persisted as items in a single collection (default fb_definitions, overridable via formsCollection). It is an ordinary user collection — not a daas_-prefixed system collection — and must exist before the builder can save:
| field | type | notes |
id | uuid (PK) | auto |
name | string | form name |
target_collection | string | collection the form targets |
key | string (nullable) | optional discriminator so multiple forms can share one collection (e.g. per issue type) |
definition | json | the FormDefinition body |
If the collection is missing, the list and builder render a FormsEmptyState hint instead of failing. With DaaS schema rights that hint offers a one-click Create collection action. Use create-collection to provision it explicitly if you prefer, on either kind of project; on a Supabase project create-migration also works (an Engine project has no migrations).
Authoring (arranging existing fields) needs only item
create/update/readon the definitions collection plusreadon the target collection's schema — not admin/DDL rights. Only provisioning (creating a collection or a new real column) needs schema rights.
Installation
The Forms module is opt-in — it is not part of bootstrap.
# 1. Add the builder + renderer + /forms pages + useFormDefinitions hook
npx @buildpad/cli@latest add form-builder forms-routes --cwd /path/to/project
# 2. Add the DaaS proxy routes it needs (if not already present)
npx @buildpad/cli@latest add api-routes --cwd /path/to/projectThe docs use the shorthand
buildpad add form-builder forms-routes; thenpx @buildpad/cli@latest add …form is the equivalent no-install invocation used across these skills.
If the CLI command fails,
@buildpad/cliIS published to npm — verify withnpm view @buildpad/cli versionbefore assuming otherwise or reaching for a local clone. The CLI fetches its registry fromraw.githubusercontent.com, so confirm the environment can reach both npm and the GitHub raw CDN. A local clone is only needed for CLI development, never to consume components.
The module must render under the authenticated layout (app/(authenticated)/ with a DaaSProviderWrapper) so DaaSProvider header injection (the signed-in user's session token) is in scope — the hooks call /api/items/* (and, when provisioning in proxy mode, /api/fields/* and /api/collections/*).
On a Supabase project, DaaS CORS must be configured first.
DynamicForm(viaCollectionForm) fetches/users/me,/permissions/me, and item endpoints from the browser withcredentials: 'include'. DaaS's defaultcors_origins: ["*"]is incompatible with credentialed requests — the browser blocks every preflight (Access-Control-Allow-Origin header must not be the wildcard '*'). Set explicit origins first (see daas-platform "CORS must use explicit origins" — Bugs 17+25 — and debugging-and-error-recovery). This is not form-builder-specific: it affects every authenticated Buildpad component.On an Engine project there is nothing to configure. On an Engine project the data API accepts requests from any origin and never with cookies, as the project is provisioned. Change no cross-origin setting, and do not add the application's origin anywhere.
The Routes
app/forms/
├── page.tsx # /forms — list of saved definitions
├── new/page.tsx # /forms/new — create (auto-create or bind existing)
├── [id]/page.tsx # /forms/[id] — the FormBuilder editor
└── [id]/fill/page.tsx # /forms/[id]/fill — render with DynamicForm to create an itemStep 1 — Author with FormBuilder
// app/forms/new/page.tsx
import { FormBuilder } from "@/components/ui/form-builder";
// Start from scratch — auto-create a full fb_ collection on save (needs schema rights):
<FormBuilder onSaved={(def) => router.push(`/forms/${def.id}`)} />
// Bind to an existing collection (hybrid storage):
<FormBuilder targetCollection="issues" onSaved={(def) => router.push(`/forms/${def.id}`)} />// app/forms/[id]/page.tsx — edit an existing definition (target comes from the definition)
<FormBuilder definitionId={id} />| Prop | Type | Purpose |
targetCollection | string | Required when creating a bound form; omit when editing or "start from scratch" |
definitionId | string | number | Edit an existing definition |
formsCollection | string (default fb_definitions) | Definitions collection name |
onSaved | (def: FormDefinition) => void | Called after a successful save with the persisted definition |
The Build tab is a three-pane workspace — field-type palette (+ Add extra field + existing collection fields) on the left, the section canvas in the middle, a per-field settings panel (width / required / hidden / conditions) on the right — plus a Preview tab that renders the in-progress form live.
Step 2 — Fill with DynamicForm
// app/forms/[id]/fill/page.tsx
"use client";
import { use } from "react";
import { useRouter } from "next/navigation";
import { DynamicForm } from "@/components/ui/form-builder";
export default function FillFormPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = use(params);
const router = useRouter();
return (
<DynamicForm
definitionId={id}
onSuccess={() => router.push("/forms")}
onCancel={() => router.push("/forms")}
/>
);
}| Prop | Type | Default | Purpose |
definitionId | string | number | — (required) | The saved definition to render |
formsCollection | string | fb_definitions | Definitions collection name |
itemId | string | number | — | Provide to switch the form to edit mode |
onSuccess | (data?) => void | — | Called after a successful create/update |
onCancel | () => void | — | Called on cancel |
scopeField | string | 'resource_uri' | Target-collection field holding the scope URI (multi-tenant) |
injectActiveScope | boolean | true | Inject the active scope URI as a create-time default |
The List View & Empty State
// app/forms/page.tsx
import { FormsEmptyState, type FormDefinition } from "@/components/ui/form-builder";
import { useFormDefinitions } from "@/lib/buildpad/hooks";
const { list } = useFormDefinitions();
// list().then(setDefinitions).catch(...) — render FormsEmptyState on error/missing collectionData Model & Hook (quick reference)
- Hook
useFormDefinitions()from@/lib/buildpad/hooks→list,get,create,update,remove,resolveScreen(the API keeps the legacy screen name for a resolved definition), plusloading/error.DEFAULT_FORMS_COLLECTION = 'fb_definitions'. - On an Engine project a list request returns at most 1000 items (
limitis capped at 1000), andremoveis answered204 No Contentwith no body. - Types
FormDefinition,FormSection,FormFieldConfigare re-exported from the component barrel@/components/ui/form-builder(as used in the list-view example above).FieldConditionis the exception — it is deliberately not re-exported from the barrel, so import it from@/lib/buildpad/types.
For the full data model, conditions cookbook, storage strategies, and multi-tenant scope behaviour, read references/form-builder.instructions.md.
See It Live
Forms Storybook (port 6010): pnpm --filter @buildpad/ui-forms storybook. The FormBuilder (DaaS) story connects to a real DaaS instance via the storybook-host proxy.
Related
- create-collection — provision the
fb_definitionsor target collections (on either kind of project). - create-feature/vform-usage — the VForm runtime the builder wraps.
- buildpad-reference — full component catalog.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

