Next.js  

How to Use Server Actions in Next.js for Form Submissions

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:

  • Submitting forms

  • Contact forms

  • Login & registration

  • Feedback systems

  • Database inserts and updates

  • Sending emails

  • File uploads

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:

FeatureServer ActionsAPI Routes
Where they runServer onlyServer only
How they are triggeredDirectly from forms or componentsCalled through HTTP requests
Best forForms, database writes, simple interactionsComplex APIs, third-party integrations
SecurityVery secure (no public endpoint)Needs manual authentication setup
PerformanceFaster due to no network callSlightly slower due to API request overhead
Client-side usageCan be called directly from UIMust use fetch/axios
Data handlingUses FormData automaticallyJSON or FormData manually parsed
File uploadsVery simple and secureRequires additional parsing libraries
When to avoidHeavy background jobsSmall/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.