Introduction
Next.js Server Actions let you run server-side code directly from your React components, without needing API routes. This feature makes form submissions faster, more secure, and easier to manage. Instead of sending data to an API endpoint, you can call a server function directly from a form. In this article, we will learn how Server Actions work and how to use them for real-world form submissions in a practical and straightforward way.
What Are Server Actions in Next.js?
Server Actions are special server-side functions that run securely on the server. They allow you to handle tasks like saving form data, inserting records into a database, sending emails, and validating user inputs—all without creating separate API routes.
Server Actions:
Run only on the server.
Cannot be accessed publicly from the browser.
Improve security by hiding business logic.
Reduce code complexity by removing unnecessary API routes.
Example structure:
'use server';
export async function submitForm(data) {
console.log('Form received:', data);
}
How to Create a Server Action
To create a Server Action, define a function inside a component or in a separate file and start it with:
'use server';
This tells Next.js to treat the function as a server-only function.
Example:
// app/actions/formActions.js
'use server';
export async function saveUser(formData) {
const name = formData.get('name');
const email = formData.get('email');
console.log('Saving user:', name, email);
}
How to Use Server Actions in a Form
Using a Server Action in a form is extremely simple. Instead of submitting to an API route, you assign the Server Action directly to the form's action attribute.
Example:
// app/register/page.jsx
import { saveUser } from '../actions/formActions';
export default function RegisterPage() {
return (
<form action={saveUser} className="space-y-4">
<input type="text" name="name" placeholder="Enter name" required />
<input type="email" name="email" placeholder="Enter email" required />
<button type="submit">Submit</button>
</form>
);
}
Now, when the user submits the form, it calls the Server Action directly.
How Server Actions Handle Form Data
The Server Action automatically receives the form data as FormData.
Example:
'use server';
export async function saveUser(formData) {
const name = formData.get('name');
const email = formData.get('email');
// Save to database
await db.user.create({ data: { name, email } });
return "User saved successfully";
}
This approach is secure because the database code runs only on the server.
Handling Validation in Server Actions
You can add validation directly in the Server Action.
'use server';
export async function saveUser(formData) {
const email = formData.get('email');
if (!email.includes('@')) {
throw new Error('Invalid email address');
}
// Continue saving
}
If validation fails, Next.js returns the error to your component.
Returning Data from Server Actions
A server action can return values like messages, objects, or redirect instructions.
Example: Return success message
return { success: true, message: "User added" };
Example: Redirect after form submit
import { redirect } from 'next/navigation';
export async function saveUser(formData) {
// Save user
redirect('/success');
}
Using Server Actions with Async UI Updates
Server Actions work perfectly with React's useTransition to show loading states.
Example:
'use client';
import { useTransition } from 'react';
import { saveUser } from '../actions/formActions';
export default function RegisterForm() {
const [isPending, startTransition] = useTransition();
return (
<form
action={(formData) => {
startTransition(() => saveUser(formData));
}}
>
<input name="name" />
<button disabled={isPending}>{isPending ? 'Saving...' : 'Save'}</button>
</form>
);
}
When Should You Use Server Actions?
Server Actions are perfect for:
Avoid using them for heavy background tasks; use Background Jobs or API routes instead.
Best Practices
Keep actions in a separate file
Helps maintain cleaner code.
Use Zod or custom validation
Ensures only valid data reaches the server.
Avoid console logs in production
Use proper logging tools.
Keep Server Actions lightweight
They should handle logic quickly and return results fast.
Real-Life Examples
Example 1: Contact Form on a Business Website
A small business in Mumbai wants a simple contact form where users can submit their name, email, and message. Instead of creating an API route, the developer uses a Server Action to handle the form submission. The action securely sends the message to the business owner’s email.
'use server';
import nodemailer from 'nodemailer';
export async function sendContact(formData) {
const name = formData.get('name');
const email = formData.get('email');
const message = formData.get('message');
const transporter = nodemailer.createTransport({ service: 'gmail', auth: { user: process.env.EMAIL, pass: process.env.PASS }});
await transporter.sendMail({
from: email,
to: process.env.EMAIL,
subject: `New Contact Message from ${name}`,
text: message,
});
return { success: true };
}
This is secure because the email credentials live only on the server.
Example 2: User Registration with Database Save
An education startup in Bengaluru wants to store student registrations. Server Actions allow them to save user details directly to a database without creating a backend API.
'use server';
import { db } from '@/lib/db';
export async function registerStudent(formData) {
const name = formData.get('name');
const course = formData.get('course');
await db.student.create({ data: { name, course }});
return { message: 'Student registered successfully' };
}
This keeps database logic secure and avoids exposing endpoints.
Server Actions vs API Routes: What’s the Difference?
Server Actions and API Routes both allow server-side operations, but they work very differently. Here’s a clear comparison:
| Feature | Server Actions | API Routes |
|---|
| Where they run | Server only | Server only |
| How they are triggered | Directly from forms or components | Called through HTTP requests |
| Best for | Forms, database writes, simple interactions | Complex APIs, third-party integrations |
| Security | Very secure (no public endpoint) | Needs manual authentication setup |
| Performance | Faster due to no network call | Slightly slower due to API request overhead |
| Client-side usage | Can be called directly from UI | Must use fetch/axios |
| Data handling | Uses FormData automatically | JSON or FormData manually parsed |
| File uploads | Very simple and secure | Requires additional parsing libraries |
| When to avoid | Heavy background jobs | Small/simple form submissions |
Simple Explanation
Choose Server Actions when dealing with forms, user inputs, or secure logic that should not be exposed.
Choose API Routes when you need a full REST API, webhooks, or third-party integrations like Stripe or Razorpay.
Summary
Server Actions in Next.js make form submissions faster, more secure, and easier by allowing forms to call server-side code directly without using API routes. By defining a Server Action with use server, connecting it to a form, and handling validation and data processing inside the action, developers can build efficient and modern forms with very little setup. This approach simplifies code, improves performance, and enhances security for applications built with the latest version of Next.js.