Form Wire
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.

Next Steps

On this page