Introduction to Next.js Server Actions

Giancarlo Buomprisco - Jun 8 '23 - - Dev Community

This article was originally posted on makerkit.dev.

The new App Router released in Next.js 13 brings with it a tons of new features that brought the famous framework by Vercel to a whole new level. We have discussed the new App Router in our article Next.js 13: complete guide to Server Components and the App Directory.

What are Next.js Server Actions?

One of the most exciting features is the introduction of Server Actions or Server Functions. Just like the word says, Server Actions are functions that run on the server, but that we can call from the client, just like a normal function. This is a huge step forward for Next.js, as it allows us to run code on the server without having to create an API endpoint. This is a whole new DX for Next.js developers, and it's going to be a game changer.

To be totally honest, this is actually not a Next.js-specific concept: rather, it's a [React.js built-in functionality, which is still in alpha.

What can I do with Next.js Server Actions?

Server Actions are a very powerful feature, and they can be used for a lot of different use cases. Here are a few examples:

  • writing to a database: you can write to a database directly from the client, without having to create an API endpoint - just by defining your logic in a server action.
  • server logic: executing any server-related business logic, such as sending emails, creating files, etc.
  • calling external APIs: you can call external APIs directly from server actions, without having to create an API endpoint

In summary, you can do anything you would normally do on the server, but without having to create an API endpoint.

Pros to using Next.js Server Actions

There are a few pros to using Next.js Server Actions:

  1. No need to create an API endpoint: you can run server code without having to create an API endpoint.
  2. Jumping to the definition: you can jump to the definition of a server action just by clicking on it in your code editor, without the need of searching for it in your codebase.
  3. Type safety: you can use TypeScript to define the arguments and return value of your server actions, and Next.js will automatically validate them for you.
  4. Less code: you can write less code, as you need a lot less boilerplate to run server code - you can just define a function and its parameters - and then call it from the client.

There's a lot to love about Next.js Server Actions, and I'm sure you'll find a lot of use cases for them.

How to define a Next.js Server Action

Server actions can normally be defined anywhere in your components, but with a few exceptions. Let's take a look at a few scenarios.

Before you start, ensure you have enabled the experimental server actions in your next.config.js file:

module.exports = {
  experimental: {
    serverActions: true,
  }
};
Enter fullscreen mode Exit fullscreen mode

Defining a Server Action in a Server Component

If you are defining a server action in a server component, the only thing you need to do is to define a function with the use server keyword at the top.

For example, the below is a valid server action:

async function myActionFunction() {
  'use server';

  // do something
}
Enter fullscreen mode Exit fullscreen mode

Very important: server actions functions should have serializable arguments and a serializable return value based on the React Server Components protocol. This is because the function's response will be serialized and sent to the client.

Defining multiple Next.js Server Actions

An alternative way to define a server action is to use export multiple functions from a file, adding the use server keyword at the top of the file.

'use server'

export async function myActionFunction() {
  // do something
}

export async function anotherActionFunction() {
  // do something
}
Enter fullscreen mode Exit fullscreen mode

Client Components can only import actions from server actions files: client components cannot define server actions inline from the same file - but you can still import them from a file that defines multiple server actions using the use server keyword.

How to invoke a Next.js Server Action

To invoke a server action, you have numerous options.

Invoking a Server Action from a Form

The simplest way to invoke a server action is to invoke it from a form. To do so, you can use the onSubmit prop of the form element, and call the server action from there.

export function MyFormComponent() {
  function handleFormAction(
    formData: FormData
  ) {
    'use server';

    const name = formData.get('name');
    // do something
  }

  return (
    <form action={handleFormAction}>
      <input type={'name'} />

      <button type="submit">Save</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Invoking a Server Action from a Button

You can also invoke a server action from a button. To do so, you can use the handleAction prop of the button element, and call the server action from there.

export function Form() {
  async function handleSubmit() {
    'use server';
    // ...
  }

  return (
    <form>
      <input type="text" name="name" />
      <button formAction={handleSubmit}>Submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Invoking a Server Action imperatively

You can also invoke a server action imperatively, by using the useTransition hook.

First, we define our server action in a separate file that defines multiple server actions:

'use server';

export async function saveData(id) {
  await addItemToDb(id);

  revalidatePath('/product/[id]');
}
Enter fullscreen mode Exit fullscreen mode

Then, we can import it in our client component, and invoke it imperatively using the useTransition hook:

'use client';

import { useTransition } from 'react';
import { saveData } from '../actions';

function ClientComponent({ id }) {
  let [isPending, startTransition] = useTransition();

  return (
    <button onClick={() => startTransition(() => saveData(id))}>
      Save
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Revalidating data after a Server Action

You can use the revalidatePath function to revalidate the data for a specific path. This is useful if you want to revalidate the data for a specific path after the server action has been executed.

Progressive Enhancement

If you use Server Actions from a form component, these will also work if JavaScript is disabled, as the form will be submitted to the server, unlike actions that are invoked imperatively, which will only work if JavaScript is enabled.

If you can, you're encouraged to use Server Actions from forms, as this will ensure your app works even if JavaScript is disabled.

Server Mutations

Next.js defines Server Mutations as Server Actions that mutate your data and calls redirect, revalidatePath, or revalidateTag.

If you are not doing any of these - you are not doing a mutation, and are not required to use useTransition: in such cases, you can just call the server action directly from your client components.

Conclusion

In this article, we've seen how to use Next.js Server Actions, and how to invoke them from client components.

You can use these today, but they are still experimental, so you should use them with caution - since the API might change in the future.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .