v1.0 — Now available on npm

Forms that

Production-ready form components for React & Next.js. Schema validation, accessible UI, loading states — all wired up and ready to use. Stop rebuilding the same forms.

$npm install formcraft
See examples ↓
12+
Components
5
Custom hooks
0ms
Config needed
AA
WCAG compliant
react-hook-formzod validationaccessible UIdark modeTypeScriptmulti-step formsfile uploadauto-savepassword strengthfield arraysloading stateserror messagesreact-hook-formzod validationaccessible UIdark modeTypeScriptmulti-step formsfile uploadauto-savepassword strengthfield arraysloading stateserror messages

StopStop rewritingrewriting thethe samesame formform code.code. FormCraftFormCraft givesgives youyou everythingeverything outout ofof thethe box.box.

— Why FormCraft

Everything you need.Nothing you don't.

Forms in React are still painful. FormCraft distills years of patterns into a single, coherent package.

01Validation

Schema-first validation

Powered by Zod with a built-in resolver. Define your schema once — FormCraft handles type inference, validation, and error display automatically.

02A11y

Accessible by default

Every component ships with ARIA labels, roles, and live regions. Screen readers, keyboard navigation, focus rings — handled from day one.

03UX

Multi-step forms

The useMultiStepForm hook gives you step management, per-step validation, and progress tracking. No extra libraries needed.

04Upload

Drag-and-drop uploads

FileUpload component with type/size validation, multiple file support, preview thumbnails, and full keyboard accessibility.

05DX

Form persistence

useFormPersist saves draft state to sessionStorage or localStorage automatically. Users never lose their work.

06DX

Auto-save

useFormAutoSave debounces saves and shows last-saved timestamps. Perfect for dashboards and CMS interfaces.

- Live demo

Every feature.
Right here.

Password strength meter, shake on error, terms checkbox — real Zod validation, zero config.

signup.tsx
const form = useFormCraft({
  schema: signupSchema,
  defaultValues: {
    name: "", email: "", password: ""
  },
});

// Shake on error, strength meter,
// password toggle -- all built-in
Zod schema
Shake errors
Pw strength
Terms check

Sign up

- Components

Browse the toolkit.

Preview

show

↑ Must be at least 3 characters

Input.tsx
<Input
  name="email"
  control={form.control}
  label="Email"
  type="email"
  leftIcon={<MailIcon />}
  required
/>

<Input
  name="password"
  control={form.control}
  label="Password"
  type="password"
  showPasswordToggle
/>

— Quick start

Up in three steps.

01

Install the package

npm install formcraft
# peer deps
npm install react react-dom zod
02

Add the stylesheet

// app/layout.tsx
import "formcraft/styles";

// Requires Tailwind in your project
// tailwind.config.js → content: [
//   "./node_modules/formcraft/
//     dist/**/*.{js,mjs}"
// ]
03

Build your first form

"use client";
import { Form, Input,
  SubmitButton, useFormCraft,
  loginSchema } from "formcraft";

export default function LoginPage() {
  const form = useFormCraft({
    schema: loginSchema,
    defaultValues: {
      email: "", password: ""
    },
  });

  return (
    <Form form={form} onSubmit={signIn}>
      <Input name="email"
        control={form.control}
        label="Email" type="email" />
      <Input name="password"
        control={form.control}
        label="Password"
        showPasswordToggle />
      <SubmitButton
        loading={form.formState
          .isSubmitting}>
        Sign in
      </SubmitButton>
    </Form>
  );
}

— Validation

Zod schemas,
pre-built.

FormCraft ships with commonly-used Zod schemas so you don't rewrite emailSchema, passwordSchema, and phoneSchema for the hundredth time.

emailSchema
passwordSchema
phoneSchema
urlSchema
requiredString()
mustBeChecked()
loginSchema
registerSchema
loginSchema

Email + password with sensible defaults

z.object({
  email: emailSchema,
  password: passwordSchema,
})
registerSchema

Full signup with password confirmation

z.object({
  name: requiredString("Name"),
  email: emailSchema,
  password: passwordSchema,
  confirmPassword: z.string(),
}).refine((d) =>
  d.password === d.confirmPassword, {
  message: "Passwords don't match",
  path: ["confirmPassword"],
})
contactSchema

Contact form with phone + message

z.object({
  name: requiredString("Name"),
  email: emailSchema,
  phone: phoneSchema.optional(),
  subject: requiredString("Subject"),
  message: z.string()
    .min(10).max(500),
  acceptTerms: mustBeChecked(
    "Please accept terms"
  ),
})

— Hooks

Five hooks.Infinite forms.

Each hook is purpose-built, tree-shakeable, and fully typed.

useFormCraftCore

Wraps react-hook-form with Zod resolver. Auto-infers types from your schema.

const form = useFormCraft({
  schema: signupSchema,
  defaultValues: { email: "" },
  mode: "onBlur",
});
useMultiStepFormUX

Step management, per-step validation, progress tracking.

const { step, next, back,
  isLast, progress }
= useMultiStepForm({ steps: 4 });
usePasswordStrengthAuth

Returns score, label, color class, and percentage for a given password string.

const strength =
  usePasswordStrength(pw);
// { score: 3, label: "Good",
//   color: "...", pct: 60 }
useFormPersistDX

Save form state to sessionStorage or localStorage. Exclude sensitive fields.

useFormPersist(form, {
  key: "signup-draft",
  storage: "sessionStorage",
  excludeFields: ["password"],
});
useFormAutoSaveDX

Debounced auto-save with last-saved timestamp.

const { isSaving, lastSaved }
= useFormAutoSave(form, {
  onSave: (data) => api.save(data),
  debounce: 1500,
});

— TypeScript

Fully typed.
Zero guessing.

Every prop, hook return value, and schema helper is typed end-to-end. The name prop is typed as FieldPath<TFieldValues> — misspell a field and TypeScript catches it instantly.

Generic TFieldValues on all components
Auto-inferred types from Zod schemas
Typed hook return values
Full IntelliSense in VS Code
login.tsx
// ✅ name is typed as FieldPath<LoginFormValues>
// TypeScript errors if field doesn't exist:

<Input
  name="emaill"    // ← TS Error: Type '"emaill"'
  control={form.control}  // is not assignable
/>                  // to FieldPath<LoginFormValues>

// ✅ Auto-inferred from Zod:
const form = useFormCraft<LoginFormValues>({
  schema: loginSchema,
  defaultValues: { email: "", password: "" },
});

// form.getValues()       → LoginFormValues ✓
// form.setValue("email") → typed ✓

— Accessibility

Built for everyone.

Accessibility isn't an afterthought in FormCraft — it's the foundation every component is built on. We test against VoiceOver, NVDA, and JAWS.

WCAG 2.1 AA

Compliant components

Full keyboard navigation

Every component — including FileUpload drag zones — is fully navigable with Tab, Enter, Space, and Arrow keys.

ARIA roles & attributes

aria-invalid, aria-describedby, role='alert', role='radiogroup', role='switch' — set automatically based on state.

Live error regions

Validation errors use aria-live='polite' so screen reader users hear them without focus moving away.

Focus-visible styles

Custom :focus-visible rings that are visible enough for low-vision users but don't interfere with mouse UX.

Reduced motion

The shake animation on error and all transitions respect prefers-reduced-motion via CSS media queries.

Auto-generated IDs

Labels are always linked to inputs via htmlFor/id pairs — auto-generated if you don't supply them.

— Get started

Install once.
Forms forever.

Join developers who stopped rebuilding the same form boilerplate and started shipping faster.

MIT licensed · Peer deps: react ≥18, zod ≥3 · TypeScript-first