committed by
GitHub
parent
784682a0f5
commit
22f78b9a86
145
.cursor/rules/forms.mdc
Normal file
145
.cursor/rules/forms.mdc
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
description: Writing Forms with Shadcn UI, Server Actions, Zod
|
||||
globs: apps/**/*.tsx,packages/**/*.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Forms
|
||||
|
||||
- Use React Hook Form for form validation and submission.
|
||||
- Use Zod for form validation.
|
||||
- Use the `zodResolver` function to resolve the Zod schema to the form.
|
||||
|
||||
Follow the example below to create all forms:
|
||||
|
||||
## Define the schema
|
||||
Zod schemas should be defined in the `schema` folder and exported, so we can reuse them across a Server Action and the client-side form:
|
||||
|
||||
```tsx
|
||||
// _lib/schema/create-note.schema.ts
|
||||
import { z } from 'zod';
|
||||
|
||||
export const CreateNoteSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
});
|
||||
```
|
||||
|
||||
## Create the Server Action
|
||||
|
||||
```tsx
|
||||
// _lib/server/server-actions.ts
|
||||
'use server';
|
||||
|
||||
import { z } from 'zod';
|
||||
import { enhanceAction } from '@kit/next/actions';
|
||||
import { CreateNoteSchema } from '../schema/create-note.schema';
|
||||
|
||||
const CreateNoteSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
});
|
||||
|
||||
export const createNoteAction = enhanceAction(
|
||||
async function (data, user) {
|
||||
// 1. "data" has been validated against the Zod schema, and it's safe to use
|
||||
// 2. "user" is the authenticated user
|
||||
|
||||
// ... your code here
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
schema: CreateNoteSchema,
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## Create the Form Component
|
||||
|
||||
Then create a client component to handle the form submission:
|
||||
|
||||
```tsx
|
||||
// _components/create-note-form.tsx
|
||||
'use client';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@kit/ui/form';
|
||||
|
||||
import { CreateNoteSchema } from '../_lib/schema/create-note.schema';
|
||||
|
||||
export function CreateNoteForm() {
|
||||
const [pending, startTransition] = useTransition();
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(CreateNoteSchema),
|
||||
defaultValues: {
|
||||
title: '',
|
||||
content: '',
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
await createNoteAction(data);
|
||||
} catch {
|
||||
// handle error
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<Form {...form}>
|
||||
<FormField name={'title'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Title</span>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<input
|
||||
type={'text'}
|
||||
className={'w-full'}
|
||||
placeholder={'Title'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
|
||||
<FormField name={'content'} render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<span className={'text-sm font-medium'}>Content</span>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
<textarea
|
||||
className={'w-full'}
|
||||
placeholder={'Content'}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)} />
|
||||
|
||||
<button disabled={pending} type={'submit'} className={'w-full'}>
|
||||
Submit
|
||||
</button>
|
||||
</Form>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Always use `@kit/ui` for writing the UI of the form.
|
||||
Reference in New Issue
Block a user