What are Server Actions in Next.js? Next.js 14 Edition

Abeer Abdul AhadAbeer Abdul Ahad
Sep 18, 2024·
4 min read

What are Server Actions?

To let you get the concept crystal clear, server actions are basically backend stuffs that you can do in your Next.js project. Server actions play a key role in web applications, handling tasks like data processing and user authentication. In Next.js, API routes offer an easy way to create custom server endpoints, making it simple to manage server-side actions. Let's explore how to implement server actions using API routes in Next.js with code examples.

Creating an API Route:

To create an API route in Next.js, you need to create a file inside the pages/api directory. The file should be named based on the desired endpoint, for example, pages/api/users.js. Within this file, you can define your server action logic.

// pages/api/users.js

export default function handler(req, res) {
  // Perform server action
  const myUsers = [
    { id: 1, name: "Odioko" },
    { id: 2, name: "Victor" },
  ];

  // Return the response
  res.status(200).json(myUsers);
}

In the code snippet above, we create an API route users.js inside the pages/api directory. Within the handler function, we perform the desired server action, such as retrieving a list of users. In this case, we define a simple array of users and return it as the response using res.status(200).json(users).

Server Actions in Server Components

In server components, we define Server Actions by adding the use server directive at the top of an async function. This tells Next.js that the function should run on the server. When a Server Action is defined directly inside a server component, it's called an Inline Server Action. Here's an example:

// app/page.tsx (Server Component)

export default function Page() {
  async function createComment() {
    'use server'
    // ...
  }

  return (
    // ... JSX for our component
  )
}

Defining Server Actions in Separate Files

For better organization, we can define Server Actions in separate files using the module-level use server directive. All functions in the file will be treated as Server Actions. Here's how:

// app/actions.ts
'use server';

export async function createComment() {
  // Logic to handle comment creation
}

Using Server Actions in Client Components

Client Components can only import actions that use the module-level use server directive.

Client Components can easily import and use Server Actions defined in separate files. For example, you can use the createComment action in a client component like this:

// app/client-component.jsx
'use client';
import { createComment } from '@/app/actions';

export default function ClientComponent() {
  // Use the handleComment Server Action here
  return <form action={createComment}>{/* ... form elements */}</form>;
}

Server Action to a Client Component as a prop

We can also pass Server Actions to Client Components as props. Here's an example of how to do it: <ClientComponent handleComment={createComment} />.

The createComment function would be a Server Action defined in the server component. This Server Action is passed as a prop (handleComment) to the client component (ClientComponent), allowing the client component to trigger that server-side action when needed (for example, when a button is clicked or a form is submitted).

This works because client components can trigger Server Actions, even though the actual execution of the action happens on the server.

Invoke a Server Action from an HTML element

We can use the action prop to invoke a Server Action from an HTML element, such as a <button> or <form>. For example, the following code will invoke the createComment Server Action when the user clicks the button:

<button type="button" action="{createComment}">Submit</button>

Server Actions are not limited to html elements and can be invoked from event handlers, useEffect and third-party libraries.

The Traditional Approach

The traditional approach consisted of these steps:

  • Client-Side Submission: We use an onSubmit handler in client components to collect and send data. This means the browser handles the form submission with JavaScript, so server components can't process the form directly.

  • API Route Dependency: To process form data server-side, we have to implement API routes. This extra layer handles the reception of form data and its subsequent database storage.

  • Client-Side Data Fetching: After submission, updating the UI with the latest data often meant initiating another client-side fetch request.

In essence, The traditional method is heavily dependent on client-side mechanisms for form handling and data management, led to an underutilisation of server components, missing out on their potential for improving performance and security through server-side execution.

A Practical Example of Server Actions

I've read many articles and discussed Server Actions with ChatGPT, but the one that stood out was by Ashwani Kumar Jha on codemancers.com. In it, he demonstrated a practical use-case of Server Actions by building a form submission project. (Check it out here)

Server Actions in Next.js 13 VS 14

Server Actions were experimental in Next.js 13. But it has been included in Next.js 14. because Server Actions are experimental in Next.js 13, you may need to enable the experimental features in your next.config.js file like this:

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

If you are in the 14th version you can directly use without needing to enable it on next.config.js file.

17
Subscribe to my newsletter

Read articles from Abeer Abdul Ahad directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abeer Abdul Ahad
Abeer Abdul Ahad

I am a Full stack developer. Currently focusing on Next.js and Backend.