factory-forms-builder
Skill9 starsChanged 4 months agoMITView on GitHub
Use when building multi-step forms, complex intake flows, drawer-CRUD forms, or anything beyond a one-shot Mantine form. Carries the factory's form conventions — react-hook-form + Zod via resolver, three Zod variants (server strict / client lenient / patch), modular section files from day one, field registry for AI-context-aware multi-step forms, declarative conditional visibility with auto-cleanup, debounced auto-save with dirty tracking, masked inputs for sensitive fields, dynamic field arrays, S3 presigned upload, `useTransition` for async submission, question numbering via context, review section with gap detection. Outputs form code that fits the house style — not bespoke form-state machinery.
In this repository42 other files it gives its agents.
- guidance/claude.mdCLAUDE.md
- factory-animationSkill
- factory-api-route-engineerSkill
- factory-apiSkill
- factory-authSkill
- factory-auth-wiring-specialistSkill
- factory-ciSkill
- factory-closeSkill
- factory-code-reviewerSkill
- factory-commitsSkill
- factory-data-layerSkill
- factory-data-pipeline-engineerSkill
- factory-data-pipelinesSkill
- factory-db-migration-engineerSkill
- factory-db-migrationSkill
- factory-db-schema-architectSkill
- factory-deploymentSkill
- factory-designSkill
- factory-entrySkill
- factory-feature-architectSkill
- factory-formsSkill
- factory-frontend-engineerSkill
- factory-frontendSkill
- factory-kit-auditSkill
- factory-llm-workflow-engineerSkill
- factory-llm-workflowsSkill
- factory-observabilitySkill
- factory-pitfallsSkill
- factory-promptingSkill
- factory-promptSkill
- factory-releaseSkill
- factory-security-engineerSkill
- factory-securitySkill
- factory-setup-linearSkill
- factory-stackSkill
- factory-standupSkill
- factory-state-modelingSkill
- factory-submitSkill
- factory-testingSkill
- factory-verification-engineerSkill
- factory-verificationSkill
- factory-voiceSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Conditional visibility?: If any field's display depends on another's value:
- Very specific: line 28
Auto-cleanup: when parent toggles off — set hidden child's value to undefined
- Distinctive: line 36
Auto-save?: If the form takes more than a couple minutes to fill out:
- Distinctive: line 44
Add/remove buttons; consider drag-reorder for ordered arrays
- Distinctive: line 184
If the user asks to add one field to an existing form or change validation on a single field, do it directly.
≈ “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
Apply the forms-builder specialist workflow. Build forms grounded in the factory's conventions, not generic react-hook-form code. Load factory-forms through the host's skill capability when needed.
How to think (in order)
- What kind of form? Pick one:
- Single-step drawer-CRUD form — create/edit one entity → use the drawer mode union from
factory-frontend.md - Single-step page form — login, settings, simple intake → vanilla react-hook-form
- Multi-step wizard — onboarding, complex intake → section files + field registry
- Inline edit — DataTable cell editing → different pattern, see
factory-frontend.md
- Single-step drawer-CRUD form — create/edit one entity → use the drawer mode union from
- How many fields?
- < 10 — inline schemas, hand-written JSX
- 10–30 — section files but no registry needed
- 30+ or AI-context-aware — field registry
- Schemas — three variants (see
factory-forms.md):- Server input — strict (real types, no empty-string fallbacks)
- Client form — lenient (accepts empty strings because controlled inputs default to
'') - Patch — partial input for updates
- Conditional visibility? If any field's display depends on another's value:
- Declarative rules in
src/lib/utils/conditional-visibility.ts - Auto-cleanup when parent toggles off — set hidden child's value to
undefined - Otherwise stale conditional data sneaks into submissions
- Declarative rules in
- Sensitive fields? SSN, EIN, government IDs, financial accounts:
- Masked input (
mask="99-9999999"etc.) - KMS encryption at rest — see
factory-security.md - Mask in display, decrypt only at the handler that returns plaintext
- Masked input (
- Auto-save? If the form takes more than a couple minutes to fill out:
- 2s debounce on field blur
- 3min safety-net interval flush
- Server accepts the lenient schema during auto-save; strict schema on submission
- Dynamic field arrays? (contacts, beneficial owners, line items)
useFieldArrayfrom react-hook-form- Max-count rule via
rules: { maxLength: { value: N, message: '...' } } - Add/remove buttons; consider drag-reorder for ordered arrays
- File uploads? Direct-to-S3 with presigned URLs (don't proxy through your server):
- Server action generates upload URL
- Client PUTs file to S3
- Server action confirms upload (records fileKey)
- Submission UX?
useTransitionwrapping the async submitisPendinggates the submit button- Toast on success / error
- Invalidate the right TanStack Query keys on success
Reference: canonical drawer-CRUD form skeleton
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod'; // or mantine-form-zod-resolver
import { customerFormSchema } from './schema';
import { useCreateCustomer, useUpdateCustomer } from './hooks';
type CustomerDrawerMode =
| { kind: 'closed' }
| { kind: 'create' }
| { kind: 'edit'; customer: Customer };
export function CustomerDrawer({ mode, onClose }: { mode: CustomerDrawerMode; onClose: () => void }) {
const form = useForm({
resolver: zodResolver(customerFormSchema),
defaultValues: emptyCustomerFormValues,
});
const createMutation = useCreateCustomer();
const updateMutation = useUpdateCustomer();
const [isPending, startTransition] = useTransition();
useEffect(() => {
if (mode.kind === 'edit') {
form.reset(toFormValues(mode.customer));
} else if (mode.kind === 'create') {
form.reset(emptyCustomerFormValues);
}
}, [mode.kind, mode.kind === 'edit' ? mode.customer.id : null]);
const onSubmit = form.handleSubmit((values) => {
startTransition(async () => {
const mutation = mode.kind === 'edit' ? updateMutation : createMutation;
const result = await mutation.mutateAsync(values);
if ('error' in result) { toast.error(result.error); return; }
toast.success(mode.kind === 'edit' ? 'Updated' : 'Created');
onClose();
});
});
return (
<Drawer opened={mode.kind !== 'closed'} onClose={onClose}>
<form onSubmit={onSubmit}>
{/* fields */}
<Button type="submit" loading={isPending}>{mode.kind === 'edit' ? 'Save' : 'Create'}</Button>
</form>
</Drawer>
);
}Reference: field registry shape (for ≥30-field forms)
// src/lib/fields/types.ts
export type FieldDef = {
id: string;
label: string;
type: 'text' | 'email' | 'masked' | 'select' | 'date' | 'checkbox' | 'array';
required?: boolean;
validation: z.ZodTypeAny;
options?: { value: string; label: string }[];
mask?: string;
sensitive?: boolean;
aiContext?: string; // for AI-augmented review / autofill
};
// src/lib/fields/sections/section-1-company.ts
export const SECTION_1_FIELDS: FieldDef[] = [
{ id: 'company_name', label: 'Legal name', type: 'text', required: true, validation: z.string().min(1) },
{ id: 'ein', label: 'EIN', type: 'masked', mask: '99-9999999', required: true, sensitive: true, validation: z.string().regex(/^\d{2}-\d{7}$/) },
// ...
];Output format
## Restated request
<one sentence>
## Form shape
- Type: <drawer-CRUD / page / multi-step / inline edit>
- Field count: <count>
- Field registry: <yes / no — why>
- Conditional visibility: <yes / no>
- Auto-save: <yes / no>
- Sensitive fields: <list, with encryption strategy>
## Schemas
- Server input: <path>
- Client form: <path>
- Patch: <path>
## Files to create or modify
<bulleted with paths>
## Code
<organized by file>
## Conventions check
- Three Zod variants: <yes>
- Drawer mode union (if applicable): <yes>
- useTransition wrapping submit: <yes>
- Conditional auto-cleanup (if applicable): <yes>
- Sensitive fields encrypted at rest: <yes / N/A>
## Open questions
<things the user should confirm>What you do NOT do
- Don't write a monolithic single-file form. Modular section files from day one.
- Don't unify server and client schemas. Server strict, client lenient.
- Don't leave stale conditional data in submissions. Auto-cleanup on visibility change.
- Don't proxy file uploads through your server. Direct-to-S3 with presigned URLs.
- Don't store SSN / PHI / government IDs in plaintext. KMS-at-rest, mask in display.
- Don't
awaitthe audit log on the submission path. Fire-and-forget. - Don't skip
useTransition. Double-submission is a real bug class. - Don't reach for the field registry for a 5-field form. Reserved for ≥30-field or AI-context-aware forms.
When the request is too small for this framework
If the user asks to add one field to an existing form or change validation on a single field, do it directly. The framework is for new forms, new sections, or substantial form-state refactors.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
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.

