- Published on
Next.js 14: Server-Side Actions with Shadcn Forms
- Authors
- Name
- Ripal & Zalak
Using Server-Side Actions with Shadcn Forms in Next.js 14
In this article, we'll explore how to seamlessly integrate server-side actions with Shadcn forms in Next.js 14. By combining the robust form validation capabilities of Zod with server actions, you can build scalable and efficient forms that ensure both client-side and server-side validation.
Why Combine Server Actions with Shadcn Forms?
- Scalable Validation: Server-side validation ensures data integrity.
- Type Safety: Using tools like Zod provides type-safe schema validation.
- Seamless UX: Error handling and feedback can be cleanly integrated into the UI.
Setting Up the Server Action
First, create a server
action for handling form submissions. This action will validate form data using Zod.
// actions.ts
'use server'
import { formSchema } from './schema'
export async function onSubmitStudent(prevState: { message: string }, formData: FormData) {
const parse = formSchema.safeParse({
firstName: formData.get('firstName'),
lastName: formData.get('lastName'),
})
if (!parse.success) {
const fieldErrors = parse.error.flatten().fieldErrors
return {
errors: fieldErrors,
message: undefined,
}
}
const data = parse.data
console.log('Validated Data:', data)
return {
message: 'Form submitted successfully!',
errors: undefined,
}
}
Handling Form State in the Component
Next, connect the server action to your Next.js component and display errors using Shadcn.
// page.tsx
import { useEffect } from "react";
import { useForm, zodResolver } from "@hookform/resolvers/zod";
import { useFormState, useFormStatus } from "react-dom";
import { formSchema } from "./schema";
import { onSubmitStudent } from "./actions";
import {
Form,
FormField,
FormItem,
FormLabel,
FormControl,
FormMessage,
Input,
Button,
} from "@shadcn/ui";
const initialState = {
errors: {
firstName: undefined,
lastName: undefined,
},
message: undefined,
};
export default function Page() {
const { pending } = useFormStatus();
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: { firstName: "", lastName: "" },
});
const [state, formAction] = useFormState(onSubmitStudent, initialState);
useEffect(() => {
if (state.errors) {
Object.entries(state.errors).forEach(([field, message]) => {
form.setError(field, { message });
});
}
}, [state.errors]);
return (
<Form {...form}>
<form action={formAction} className="space-y-8">
<FormField
control={form.control}
name="firstName"
render={({ field }) => (
<FormItem>
<FormLabel>First Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage>{state.errors?.firstName}</FormMessage>
</FormItem>
)}
/>
<FormField
control={form.control}
name="lastName"
render={({ field }) => (
<FormItem>
<FormLabel>Last Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage>{state.errors?.lastName}</FormMessage>
</FormItem>
)}
/>
<Button type="submit" disabled={pending}>
Submit
</Button>
</form>
</Form>
);
}
Key Considerations
Type-Safe State Management: Initialize the
errors
state with a type-safe structure:const initialState = { errors: { firstName: undefined, lastName: undefined, }, message: undefined, }
Custom Error Messages: Include server-side errors like
"User not found"
:return { errors: undefined, message: 'User not found.', }
Flattened Error Handling: Use
parse.error.flatten()
for cleaner error structures.
FAQs
How do server actions differ from traditional API calls?
Server actions in Next.js 14 allow you to handle form submissions without defining API routes explicitly. The logic resides in the server action, making the architecture simpler and more cohesive.
Can I combine client-side and server-side validation?
Yes, you can use client-side validation for immediate feedback and server-side validation for additional security and data integrity.
What are the advantages of using Zod?
Zod provides type-safe schema validation, ensuring that your form inputs adhere to predefined rules.
Conclusion
By combining Shadcn forms, Next.js 14 server actions, and Zod, you can create robust and scalable forms with seamless validation. The approach outlined here ensures type safety, better error handling, and a cleaner architecture.
Suggested Improvements
- Add more detailed error messages for better UX.
- Consider integrating with a state management library for complex forms.