Introduction

Next.js is a popular framework for building React applications with features like server-side rendering, static site generation, and hybrid rendering. Next.js also provides a way to create custom API routes that can handle requests from the client side.

However, sometimes you may want to perform some server-side data mutations or actions without creating a separate API route. For example, you may want to add an item to a shopping cart, update a user profile, or submit a form. In these cases, you can use Server Actions, an alpha feature in Next.js 13.4 that is built on top of React Actions.

What are Server Actions?

Server Actions are functions that can be defined inside Server Components or in a separate file and can be called from Client Components. They enable server-side data mutations, reduced client-side JavaScript, and progressively enhanced forms.

Server Actions have some advantages over traditional API routes:

  • They are easier to write and use. You don't need to create a separate file, define the request method, parse the request body, or send the response. You just write an async function with the use server directive and pass it as an action prop to a form or a button component.
  • They are more secure. You don't need to expose your API endpoints to the public or worry about CSRF attacks. Server Actions are only accessible from your Next.js app and are automatically protected by the same-origin policy.
  • They are more performant. You don't need to send extra HTTP requests or load extra JavaScript bundles for your API routes. Server Actions are executed on the server and only send the minimal data needed to update the UI.

How to use Server Actions?

To use Server Actions in your Next.js project, you need to enable the experimental serverActions flag in your next.config.js file:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
  },
};

module.exports = nextConfig;

Then, you can define your Server Actions in two ways:

  • Inside the component that uses it (Server Components only)
  • In a separate file (Client and Server Components), for reusability

You can define multiple Server Actions in a single file.

Inside Server Components

Create a Server Action by defining an async function with the use server directive at the top of the function body. This function should have serializable arguments and a serializable return value based on the React Server Components protocol.

For example, let's say we want to create a Server Action that increments a counter variable on the server and updates the UI accordingly. We can define it inside our Server Component like this:

import { revalidatePath } from "next/cache";

let count = 0;

export default function Counter() {
  async function increment() {
    "use server";
    count++;
    revalidatePath("/");
  }

  return (
    <div className="p-4 flex flex-col gap-4">
      <h1 className="text-2xl font-bold">Server Count: {count}</h1>
      <form action={increment}>
        <button
          type="submit"
          className="border px-4 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-500">
          Increment
        </button>
      </form>
    </div>
  );
}

Notice that we use revalidatePath to tell Next.js to re-render the page with the updated data after the Server Action is executed.

In separate files

If you want to use a Server Action inside a Client Component or reuse it across multiple components, you need to create it in a separate file with the use server directive at the top of the file. Then, you can import it into your components and use it as an action prop.

For example, let's say we want to create a Server Action that adds an item to a shopping cart on the server and updates the UI accordingly. We can create it in a separate file like this:

"use server";

import { cookies } from "next/headers";
import { saveToDb } from "./db";

export async function addItem(data) {
  const cartId = cookies().get("cartId")?.value;
  await saveToDb({ cartId, data });
}

Then, we can import it into our Client Component and use it like this:

"use client";

import { addItem } from "./actions";

export default function AddToCart({ productId }) {
  return (
    <form action={addItem}>
      <button
        type="submit"
        className="border px-4 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-500"
      >
        Add to Cart
      </button>
    </form>
  );
}

Conclusion

Server Actions are a powerful feature in Next.js 13.4 that allow you to perform server-side data mutations or actions without creating separate API routes. They are easy to write and use, secure, and performant. They also enable progressive enhancement for your forms and buttons, making them work even without JavaScript enabled in the browser.

If you want to learn more about Server Actions, you can check out the official documentation.