The Power of Server Actions in Next.js: A Comprehensive Guide

Andry Dina
Next.js server-action

Introduction

Developing a project involving Next.js' action server poses a substantial challenge. The process requires significant time investment due to the need to handle both the backend and frontend aspects of the application concurrently.

Next.js has introduced Server Actions in version 13.4, a ground-breaking feature that's transforming web development. By enabling server-side data manipulation and minimizing client-side JavaScript, Server Actions enhance both performance and security. This innovation has made Next.js a standout player in the ever-changing world of web development.

What are Server Actions in Next.js?

Next.js' Server Actions enable you to create server-side functions that you can call from the client-side. This allows you to interact with your backend securely and efficiently. Server Actions can retrieve data, transform it, and return responses all within the Next.js environment, streamlining backend interactions.

Step-by-Step Guide to Implementing Server Actions

Step 1: Setting Up Your Next.js Project

To get started, you'll need a Next.js project. If you haven't already set one up, you can create a new project with the following command:

1npx create-next-app@latest my-nextjs-app
2cd my-nextjs-app

Step 2: Creating Your Server Action

Next, let's create a server action. Server Actions are functions that run on the server but are invoked from the client. These functions are written in app directory files and are defined using the async keyword.

1// app/server/login
2'use server';
3import { someDatabaseFunction } from 'database'; //Your database
4
5export async function loginForm(data) {
6  // Perform server-side logic here, like database operations
7  const result = await someDatabaseFunction(data);
8  if(!result) throw new Error('Invalid credentials');
9
10  return result;
11}

Step 3: Creating a Form in Your Component

Now that you have a server action ready, let's create a form in your component that will send data to this server action.

1'use client';
2import { useRouter } from 'next/navigation';
3import { Button } from '@/components/ui/button';
4
5export default function ContactForm() {
6  const router = useRouter();
7
8  const handleSubmit = async (event) => {
9    event.preventDefault();
10    const formData = new FormData(event.target);
11    const result = await submitForm({
12      name: formData.get('name'),
13      email: formData.get('email'),
14      message: formData.get('message')
15    });
16
17    if (result.success) {
18      router.push('/thank-you');
19    } else {
20      console.error('Submission failed');
21    }
22  };
23
24  return (
25    <form onSubmit={handleSubmit}>
26      <input type='text' name='name' placeholder='Your Name' required />
27      <input type='email' name='email' placeholder='Your Email' required />
28      <textarea name='message' placeholder='Your Message' required></textarea>
29      <Button type='submit'>Submit</Button>
30    </form>
31  );
32}

Step 4: Handling Server Responses

Upon completion of data processing by the server action, it sends a response. In the given example, we evaluate the response to determine if the submission was successful. If it was, we redirect the user to a thank-you page.

Step 5: Integrating Components from the Shadcn Framework

Make your Next.js forms look great with Shadcn components. it provides a library of ready-to-use UI elements that can be effortlessly integrated into your forms, offering an aesthetically pleasing and professional user experience.

In this example, we use zod for schema validation, react-hook-form for handling form state, and Shadcn UI components for the form interface. This setup provides a robust, type-safe form handling mechanism in Next.js.

1import { zodResolver } from "@hookform/resolvers/zod"
2import { useForm } from "react-hook-form"
3import { z } from "zod"
4
5import { Button } from "@/components/ui/button"
6import {
7    Form,
8    FormControl,
9    FormField,
10    FormItem,
11    FormLabel,
12    FormMessage,
13} from "@/components/ui/form"
14import { Input } from "@/components/ui/input"
15import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
16import { loginForm } from "@/app/server/login"
17
18const FormSchema = z.object({
19    email: z.string().email({ message: "Please enter a valid email" }),
20    name: z.string().min(1, { message: "Please enter your name" }),
21})
22
23export function InputForm() {
24    const form = useForm<z.infer<typeof FormSchema>>({
25        resolver: zodResolver(FormSchema),
26        defaultValues: {
27            email: "",
28            name: ""
29        },
30    })
31
32    async function onSubmit(data: z.infer<typeof FormSchema>) {
33        try {
34            console.log(data)
35            const result = await loginForm(data)
36           if(result){
37              toast.success("Login success")
38           }else {
39              toast.error("Invalid credentials, please verify your email.")
40          }
41        } catch (error) {
42            if (error instanceof Error) {
43                toast.error(error.message)
44            }
45        }
46    }
47
48    return (
49        <Card>
50            <CardHeader>
51                <CardTitle>Input Form</CardTitle>
52            </CardHeader>
53            <CardContent>
54                <Form {...form}>
55                    <form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
56                        <FormField
57                            control={form.control}
58                            name="email"
59                            render={({ field }) => (
60                                <FormItem>
61                                    <FormLabel>Email</FormLabel>
62                                    <FormControl>
63                                        <Input type="email" placeholder="shadcn" {...field} />
64                                    </FormControl>
65                                    <FormMessage />
66                                </FormItem>
67                            )}
68                        />
69                        <FormField
70                            control={form.control}
71                            name="name"
72                            render={({ field }) => (
73                                <FormItem>
74                                    <FormLabel>Your name</FormLabel>
75                                    <FormControl>
76                                        <Input type="name" placeholder="shadcn" {...field} />
77                                    </FormControl>
78                                    <FormMessage />
79                                </FormItem>
80                            )}
81                        />
82                        <Button type="submit">Submit</Button>
83                    </form>
84                </Form>
85            </CardContent>
86        </Card>
87    )
88}

Form more info for the Building forms with React Hook Form and Zod, follow the page Shadcn UI form with more example.

Conclusion

Next.js Server Actions allow developers to seamlessly execute server-side tasks directly from the front end. This simplifies the development process and enables the creation of sophisticated and interactive web applications that combine exceptional design and functionality. They work seamlessly with Shadow Components and other components to elevate the user experience.

This guide has equipped you with the knowledge to use Server Actions in Next.js. Explore further and explore the potential of Next.js. Their capabilities will enhance your web development skills and unlock countless opportunities.

📢 Resource :

Join our newsletter for the
latest update

By subscribing you agree to receive the Paddle newsletter. Unsubscribe at any time.