Back to list
tech-with-seth

create-form

by tech-with-seth

React Router 7 starter with Polar.sh, BetterAuth, Prisma, and Tailwind

1🍴 0📅 Jan 24, 2026

SKILL.md


name: create-form description: Set up hybrid client/server validated forms with Zod and React Hook Form. Use when creating forms with validation.

Create Form

When to Use

  • Creating any form with validation
  • User asks to "add a form" or "create a form"

Critical Rule

// CORRECT - Use <form> with manual fetcher.submit()
<form onSubmit={handleSubmit(onSubmit)}>

// WRONG - Causes submission conflicts
<fetcher.Form onSubmit={handleSubmit(onSubmit)}>

Core Pattern

  1. Same Zod schema on client and server
  2. Client: Instant feedback with React Hook Form
  3. Server: Security with validateFormData()
  4. Auto error sync: Server errors populate form fields

Quick Start

1. Schema (app/lib/validations.ts)

import { z } from 'zod';

export const contactFormSchema = z.object({
    name: z.string().min(1, 'Name is required'),
    email: z.string().email('Invalid email'),
});

export type ContactFormData = z.infer<typeof contactFormSchema>;

2. Server Action

import { validateFormData } from '~/lib/form-validation.server';
import { zodResolver } from '@hookform/resolvers/zod';

export async function action({ request }: Route.ActionArgs) {
    const formData = await request.formData();
    const { data, errors } = await validateFormData<ContactFormData>(
        formData,
        zodResolver(contactFormSchema)
    );

    if (errors) return data({ errors }, { status: 400 });

    await processData(data!);
    return redirect('/success');
}

3. Client Form

import { useFetcher } from 'react-router';
import { useValidatedForm } from '~/lib/form-hooks';

export default function ContactPage() {
    const fetcher = useFetcher();
    const { register, handleSubmit, formState: { errors } } = useValidatedForm({
        resolver: zodResolver(contactFormSchema),
        errors: fetcher.data?.errors,
    });

    const onSubmit = (data: ContactFormData) => {
        const formData = new FormData();
        formData.append('name', data.name);
        fetcher.submit(formData, { method: 'POST' });
    };

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <TextInput {...register('name')} error={errors.name?.message} />
            <Button type="submit">Submit</Button>
        </form>
    );
}

Checklist

  1. Define Zod schema in app/lib/validations.ts
  2. Add server action with validateFormData()
  3. Use useValidatedForm hook in component
  4. Use <form> (not <fetcher.Form>) with handleSubmit

Templates

Full Reference

See .github/instructions/form-validation.instructions.md for:

  • Field-level vs form-level errors
  • Conditional validation
  • File uploads
  • Complex schema patterns

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon