Next.js Actions Tutorial: A Step-by-Step Guide
Server Actions are asynchronous functions defined in server components that can be invoked from client components.
Next.js, introduced Server Actions in version 14, enabling developers to execute server-side code seamlessly without needing dedicated API routes. This feature enhances the efficiency of handling tasks like form submissions, data fetching, and interactions with databases directly from the client side.
What are Server Actions?
Server Actions are asynchronous functions defined in server components that can be invoked from client components. They allow for operations such as:
- Form submissions
- Data mutations
- API calls
- Database interactions
To enable Server Actions in your Next.js project, you need to configure your next.config.js
module.exports = {
experimental: {
serverActions: true,
},
};
Creating a Simple Todo Application
To illustrate the use of Server Actions, let’s create a simple Todo application where users can add tasks via a form.
Step 1: Create the Form Component
First, create a form for adding new tasks. This will be a controlled component that submits data to our Server Action
// components/TodoForm.js
'use client';
import { useFormStatus } from 'react-dom';
const TodoForm = () => {
const { pending } = useFormStatus();
return (
<form action={addTodo}>
<div className="flex flex-col mb-4">
<label htmlFor="new-todo" className="mb-2 text-lg font-medium">
Add a new task
</label>
<input
type="text"
id="new-todo"
name="new-todo"
className="p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Enter your task"
/>
</div>
<button
type="submit"
disabled={pending}
className="w-full py-2 px-4 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none"
>
Add Task
</button>
</form>
);
};
export default TodoForm;
Step 2: Implement the Server Action
Next, define the addTodo
function that will handle the form submission on the server
// actions/addTodo.js
'use server';
export async function addTodo(formData) {
const task = formData.get('new-todo');
// Here you would typically interact with your database
console.log(`New task added: ${task}`);
// Simulate adding to a database
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulating async DB operation
return { success: true };
}
Step 3: Integrate the Form and Action
// pages/index.js
import TodoForm from '../components/TodoForm';
import { addTodo } from '../actions/addTodo';
export default function Home() {
return (
<div>
<h1 className="text-2xl font-bold">Todo List</h1>
<TodoForm />
</div>
);
}
Enhancing User Experience with Loading States
Using the useFormStatus
hook allows you to manage loading states effectively. The submit button is disabled while the form is being processed, providing feedback to users.
import React from "react";
import { useFormStatus } from "react-dom";
function UserForm() {
const { pending } = useFormStatus();
const handleSubmit = async (event) => {
event.preventDefault();
// Simulate an asynchronous operation (e.g., API call)
await new Promise((resolve) => setTimeout(resolve, 2000));
alert("Form submitted successfully!");
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="username">Username:</label>
<input type="text" id="username" name="username" required />
</div>
<div>
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" required />
</div>
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
</form>
);
}
export default UserForm;
Conclusion
Next.js Server Actions simplify server-side operations by allowing developers to define functions that can be called directly from client components without the need for complex API setups.