Getting Started
Quick Start
Build your first form in under 5 minutes
1. Define a Schema
import { z } from "zod";
const contactSchema = z.object({
name: z.string().min(1, "Enter a name"),
email: z.email("Enter a valid email"),
message: z.string().min(1, "Enter a message"),
});2. Create a Server Action
"use server";
import { createAction, formError } from "@form-wire/core";
import { contactSchema } from "./schema";
export const submitContact = createAction(contactSchema, async (data) => {
// data is fully typed as { name: string; email: string; message: string }
await saveToDatabase(data);
return { message: "Message sent!" };
});3. Create a Form Wire
"use client";
import { createFormWire } from "@form-wire/react";
import { contactSchema } from "./schema";
const Contact = createFormWire(contactSchema, {
fields: {
name: { label: "Name", placeholder: "Ada Lovelace" },
email: { label: "Email", placeholder: "ada@form.dev" },
message: { label: "Message", component: "textarea" },
},
});4. Render the Form
The simplest way is to let Form Wire auto-render all fields:
"use client";
import { FormWireProvider, htmlMapper } from "@form-wire/react";
import { Contact } from "./contact";
import { submitContact } from "./action";
export function ContactPage() {
return (
<FormWireProvider mapper={htmlMapper}>
<Contact.Form action={submitContact} />
</FormWireProvider>
);
}htmlMapper renders unstyled HTML inputs. For production UIs, swap it with a shadcn/ui mapper or your own.
5. Manual Field Layout
For custom layouts, place individual fields:
<Contact.Form action={submitContact}>
<Contact.Field name="name" />
<Contact.Field name="email" />
<Contact.Field name="message" />
</Contact.Form>You can mix explicit fields with auto-rendering — Contact.Fields renders only the fields you name, and any unnamed visible fields are auto-appended at the end.