Skip to content

    Server Actions

    Server Actions are an alpha feature in Next.js, built on top of React Actions. They enable server-side data mutations, reduced client-side JavaScript, and progressively enhanced forms.

    app/add-to-cart.js
    import { cookies } from 'next/headers';
     
    export default function AddToCart({ productId }) {
      async function addItem(data) {
        'use server';
     
        const cartId = cookies().get('cartId')?.value;
        await saveToDb({ cartId, data });
      }
     
      return (
        <form action={addItem}>
          <button type="submit">Add to Cart</button>
        </form>
      );
    }

    Convention

    You can enable Server Actions in your Next.js project by enabling the experimental serverActions flag.

    next.config.js
    module.exports = {
      experimental: {
        serverActions: true,
      },
    };

    Creation

    Create a Server Action by defining an asynchronous 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.

    app/components/component.js
    async function myAction() {
      'use server';
      // ...
    }

    You can also use a top-level "use server" directive on top of a file. This can be useful if you have a single file that exports multiple Server Actions, and it is required if you're importing a Server Action in a Client Component.

    app/actions.js
    'use server';
     
    export async function myAction() {
      // ...
    }

    Note: When using a top-level "use server" directive, all exports will be considered Server Actions.

    Invocation

    You can invoke Server Actions using the following methods:

    • Using action: React's action prop allows invoking a Server Action on a <form> element.
    • Using formAction: React's formAction prop allows handling <button>, <input type="submit">, and <input type="image"> elements in a <form>.
    • Custom Invocation with startTransition: Invoke Server Actions without using action or formAction by using startTransition. This method disables Progressive Enhancement.

    action

    You can use React's action prop to invoke a Server Action on a form element. Server Actions passed with the action prop act as asynchronous side effects in response to user interaction.

    app/add-to-cart.js
    export default function AddToCart({ productId }) {
      async function addItem(data) {
        'use server';
     
        const cartId = cookies().get('cartId')?.value;
        await saveToDb({ cartId, data });
      }
     
      return (
        <form action={addItem}>
          <button type="submit">Add to Cart</button>
        </form>
      );
    }

    Note: An action is similar to the HTML primitive action

    formAction

    You can use formAction prop to handle Form Actions on elements such as button, input type="submit", and input type="image". The formAction prop takes presedence over the form's action.

    app/form
    export default function Form() {
      async function handleSubmit() {
        'use server';
        // ...
      }
     
      async function submitImage() {
        'use server';
        // ...
      }
     
      return (
        <form action={handleSubmit}>
          <input type="text" name="name" />
          <input type="image" formAction={submitImage} />
          <button type="submit">Submit</button>
        </form>
      );
    }

    Note: A formAction is the HTML primitive formaction. React now allows you to pass functions to this attribute.

    Custom invocation using startTransition

    You can also invoke Server Actions without using action or formAction. You can achieve this by using startTransition provided by the useTransition hook, which can be useful if you want to use Server Actions outside of forms, buttons, or inputs.

    Note: Using startTransition disables the out-of-the-box Progressive Enhancement.

    app/components/example-client-component.js
    'use client';
     
    import { useTransition } from 'react';
    import { addItem } from '../actions';
     
    function ExampleClientComponent({ id }) {
      let [isPending, startTransition] = useTransition();
     
      return (
        <button onClick={() => startTransition(() => addItem(id))}>
          Add To Cart
        </button>
      );
    }
    app/actions.js
    'use server';
     
    export async function addItem(id) {
      await addItemToDb(id);
      revalidatePath(`/product/${id}`);
    }

    Custom invocation without startTransition

    If you aren't doing Server Mutations, you can directly pass the function as a prop like any other function.

    app/posts/[id]/page.tsx
    import kv from '@vercel/kv';
    import LikeButton from './like-button';
     
    export default function Page({ params }: { params: { id: string } }) {
      async function increment() {
        'use server';
        await kv.incr(`post:id:${params.id}`);
      }
     
      return <LikeButton increment={increment} />;
    }
    app/post/[id]/like-button.tsx
    'use client';
     
    export default function LikeButton({
      increment,
    }: {
      increment: () => Promise<void>;
    }) {
      return (
        <button
          onClick={async () => {
            await increment();
          }}
        >
          Like
        </button>
      );
    }

    Enhancements

    Experimental useOptimistic

    The experimental useOptimistic hook provides a way to implement optimistic updates in your application. Optimistic updates are a technique that enhances user experience by making the app appear more responsive.

    When a Server Action is invoked, the UI is updated immediately to reflect the expected outcome, instead of waiting for the Server Action's response.

    app/thread.js
    'use client';
     
    import { experimental_useOptimistic as useOptimistic } from 'react';
    import { send } from './actions.js';
     
    export function Thread({ messages }) {
      const [optimisticMessages, addOptimisticMessage] = useOptimistic(
        messages,
        (state, newMessage) => [...state, { message: newMessage, sending: true }],
      );
      const formRef = useRef();
     
      return (
        <div>
          {optimisticMessages.map((m) => (
            <div>
              {m.message}
              {m.sending ? 'Sending...' : ''}
            </div>
          ))}
          <form
            action={async (formData) => {
              const message = formData.get('message');
              formRef.current.reset();
              addOptimisticMessage(message);
              await send(message);
            }}
            ref={formRef}
          >
            <input type="text" name="message" />
          </form>
        </div>
      );
    }

    Experimental useFormStatus

    The experimental useFormStatus hook can be used within Form Actions, and provides the pending property.

    app/form.js
    import { experimental_useFormStatus as useFormStatus } from 'react-dom';
     
    function Submit() {
      const { pending } = useFormStatus();
     
      return (
        <input
          type="submit"
          className={pending ? 'button-pending' : 'button-normal'}
          disabled={pending}
        >
          Submit
        </input>
      );
    }

    Progressive Enhancement

    Progressive Enhancement allows a <form> to function properly without JavaScript, or with JavaScript disabled. This allows users to interact with the form and submit data even if the JavaScript for the form hasn't been loaded yet or if it fails to load.

    Both Server Form Actions and Client Form Actions support Progressive Enhancement, using one of two strategies:

    • If a Server Action is passed directly to a <form>, the form is interactive even if JavaScript is disabled.
    • If a Client Action is passed to a <form>, the form is still interactive, but the action will be placed in a queue until the form has hydrated. The <form> is prioritized with Selective Hydration, so it happens quickly.
    app/components/example-client-component.js
    'use client';
     
    import { useState } from 'react';
    import { handleSubmit } from './actions.js';
     
    export default function ExampleClientComponent({ myAction }) {
      const [input, setInput] = useState();
     
      return (
        <form action={handleSubmit} onChange={(e) => setInput(e.target.value)}>
          {/* ... */}
        </form>
      );
    }

    In both cases, the form is interactive before hydration occurs. Although Server Actions have the additional benefit of not relying on client JavaScript, you can still compose additional behavior with Client Actions where desired without sacrificing interactivity.

    Examples

    Usage with Client Components

    Import

    Server Actions cannot be defined within Client Components, but they can be imported. To use Server Actions in Client Components, you can import the action from a file containing a top-level "use server" directive.

    app/actions.js
    'use server';
     
    export async function addItem() {
      // ...
    }
    app/components/example-client-component.js
    'use client';
     
    import { useTransition } from 'react';
    import { addItem } from '../actions';
     
    function ExampleClientComponent({ id }) {
      let [isPending, startTransition] = useTransition();
     
      return (
        <button onClick={() => startTransition(() => addItem(id))}>
          Add To Cart
        </button>
      );
    }

    Props

    Although importing Server Actions is recommended, in some cases you might want to pass down a Server Action to a Client Component as a prop.

    For example, you might want to use a dynamically generated value within the action. In that case, passing a Server Action down as a prop might be a viable solution.

    app/components/example-server-component.js
    import { ExampleClientComponent } from './components/example-client-component.js';
     
    function ExampleServerComponent({ id }) {
      async function updateItem(data) {
        'use server';
        modifyItem({ id, data });
      }
     
      return <ExampleClientComponent updateItem={updateItem} />;
    }
    app/components/example-client-component.js
    'use client';
     
    function ExampleClientComponent({ updateItem }) {
      async function action(formData: FormData) {
        await updateItem(formData);
      }
     
      return (
        <form action={action}>
          <input type="text" name="name" />
          <button type="submit">Update Item</button>
        </form>
      );
    }

    On-demand Revalidation

    Server Actions can be used to revalidate data on-demand by path (revalidatePath) or by cache tag (revalidateTag).

    import { revalidateTag } from 'next/cache';
     
    async function revalidate() {
      'use server';
      revalidateTag('blog-posts');
    }

    Validation

    The data passed to a Server Action can be validated or sanitized before invoking the action. For example, you can create a wrapper function that receives the action as its argument, and returns a function that invokes the action if it's valid.

    app/actions.js
    'use server';
     
    import { withValidate } from 'lib/form-validation';
     
    export const action = withValidate((data) => {
      // ...
    });
    lib/form-validation
    export function withValidate(action) {
      return (formData: FormData) => {
        'use server';
     
        const isValidData = verifyData(formData);
     
        if (!isValidData) {
          throw new Error('Invalid input.');
        }
     
        const data = process(formData);
        return action(data);
      };
    }

    Using headers

    You can read incoming request headers such as cookies and headers within a Server Action.

    import { cookies } from 'next/headers';
     
    async function addItem(data) {
      'use server';
     
      const cartId = cookies().get('cartId')?.value;
     
      await saveToDb({ cartId, data });
    }

    Additionally, you can modify cookies within a Server Action.

    import { cookies } from 'next/headers';
     
    async function create(data) {
      'use server';
     
      const cart = await createCart():
      cookies().set('cartId', cart.id)
      // or
      cookies().set({
        name: 'cartId',
        value: cart.id,
        httpOnly: true,
        path: '/'
      })
    }

    Glossary

    Actions

    Performs asynchronous side effects in response to user interaction, with built-in solutions for error handling and optimistic updates. Similar to the HTML primitive action.

    Form Actions

    Actions integrated into the web standard <form> API, and enable out-of-the-box progressive enhancement and loading states. Similar to the HTML primitive formaction.

    Server Functions

    Functions that run on the server, but can be called on the client.

    Server Actions

    Server Functions called as an action.

    Server Mutations

    Server Actions that mutates your data and calls redirect, revalidatePath, or revalidateTag.

    Next Steps

    For more information on what to do next, we recommend the following sections

    Was this helpful?