Think Server Actions Can Only Be Used in Server Components? Think again

Rishi BakshiRishi Bakshi
3 min read

One common misconception in Next.js development is thinking that server actions can only be utilized within server components. However, this isn't the case. Server actions can be used inside client components as well, which allows us to interact with server-side functionality directly from client-side user interactions.

Let's break this down with an intuitive example.


Example: Using Server Actions in Client Components

Imagine we have a to-do app. We're using Prisma to manage our database. Now, say we want to add a new to-do item via a button inside a client component. Here's how we can implement this.

  1. Define the Server Action: We start by writing a server action that adds a new to-do item to the database.
// /app/actions/todo.tsx

"use server";

import { prisma } from "@/lib/prisma";

export async function addTodoAction(todoText: string) {
    await prisma.todo.create({
        data: {
            text: todoText,
            completed: false,
        },
    });
}

This function uses Prisma to add a new to-do to our database. The important part is the "use server" directive, which makes this a server action.


  1. Attach the Server Action to a Client Component: Now, we can utilize this server action inside a client component that contains a form and a button to submit the new to-do.
// app/components/TodoForm.tsx

"use client";

import { addTodoAction } from "@/app/actions/todo.tsx";
import { useState } from "react";

const TodoForm = () => {
    const [todoText, setTodoText] = useState("");

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();

        // Call the server action
        await addTodoAction(todoText);

        // Clear the input field after submission
        setTodoText("");
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={todoText}
                onChange={(e) => setTodoText(e.target.value)}
                placeholder="Enter new to-do"
            />
            <button type="submit">Add To-Do</button>
        </form>
    );
};

export default TodoForm;

In this component:

  • We use the useState hook to track the input value.

  • On form submission, we invoke the addTodoAction server action, passing the input data (todoText) to it.

  • The server action takes care of the actual database operation on the server side.


What Happens Under the Hood?

When the button is clicked:

  1. The client component sends the todoText to the server action.

  2. Next.js handles the network boundary, transferring the data to the server.

  3. The server action executes on the server where the app is hosted, ensuring that sensitive database operations happen safely.

  4. Once the database update is complete, the client-side UI can react to the changes (e.g., by clearing the input or triggering a re-render).


Key Takeaway: Server Actions Work in Client Components Too

The takeaway here is that server actions aren't limited to server components. You can trigger them directly from client components. This opens up a flexible approach to handle server-side operations while keeping client-side interactions smooth and responsive.

Important Notes:

  • Always ensure that the server action is handling sensitive operations like database updates, as it is only accessible on the server side.

  • By utilizing server actions in client components, you can keep your data flow secure while providing seamless user experiences.

0
Subscribe to my newsletter

Read articles from Rishi Bakshi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rishi Bakshi
Rishi Bakshi

Full Stack Developer with experience in building end-to-end encrypted chat services. Currently dedicated in improving my DSA skills to become a better problem solver and deliver more efficient, scalable solutions.