Teachnique
      CourseRoadmaps
      Login

      Introduction

      Project SetupCLI

      IntroductionAPP RouterTemplateLoading UIError Handling404 Page

      Linking and Navigating

      Dynamic RoutesRoute groupsParallel RoutesIntercepting Routes

      Route handlersCacheCommon Questions

      Middleware

      CSRSSR & SSGISR

      React Server ComponentsServer-Side Rendering

      SuspenseStreaming

      Server & Client ComponentServer vs. Client ComponentBest Practices

      Server-Side Rendering Strategies

      Data Fetching & Caching & RedirectThird-Party Request Libraries on the Server SideBest Practices

      Request MemoizationData CacheFull Route CacheRouter Cache

      Server Actions IFormServer Actions IIOptimistic Update

      Tailwind CSSCSS-in-JSSass

      Environment VariablesAbsolute Imports& Module Path AliasesRoute Segment Config

      Image & LCPImage IImage IIOptimizing IOptimizing II

      Next FontFont ParameterApply Styles

      Feedback

      Submit request if you have any questions.

      Course
      Server Actions I

      Next.js Mastery: From Fundamentals to Full-Stack

      Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.

      Server Actions

      Server Actions refer to asynchronous functions that are executed on the server side. They can be used in both server and client components in Next.js applications to handle data submissions and mutations.
      Note: "data query" refers to reading data. Though "data mutation" might sound unfamiliar at first, it’s a key concept in handling data changes in applications.

      Basic Usage


      
      To define a Server Action, you need to use the "use server" directive in React. There are two ways to apply this directive:
      1. Function-Level Usage: Place "use server" at the top of an async function to designate it as a Server Action.
      2. Module-Level Usage: Place "use server" at the top of a file to make all exported functions from that file Server Actions.
      Server Actions can be used in both server and client components:
      In Server Components: Both function-level and module-level usages are supported.
      // app/page.jsx
      export default function Page() {
      // Server Action
      async function create() {
      'use server'
      // ...
      }
      return (
      // ...
      )
      }
      In Client Components: Only module-level usage is supported. You need to create a separate file, commonly named "actions," and apply the "use server" directive at the top.
      'use server'
      
      // app/actions.js
      export async function create() {
      // ...
      }
      And then import the file like this:
      import { create } from '@/app/actions'
      export function Button() {
      return (
      // ...
      )
      }
      You can also pass Server Actions as props to client components:
      <ClientComponent updateItem={updateItem} />
      'use client'
      
      export default function ClientComponent({ updateItem }) {
      return <form action={updateItem}>{/* ... */}</form>
      }

      Use Cases


      
      In the Pages Router, interacting with the backend typically requires defining an API endpoint and making calls to it. However, in the App Router, these operations can be simplified using Server Actions. If you are implementing functionality that would traditionally require creating an API for frontend-backend communication, consider using Server Actions—unless you specifically need an external API.
      
      Server Actions are often used with <form> elements, but they can also be triggered within event handlers, useEffect, third-party libraries, or other form elements like <button>.

      Practical Example


      
      Let’s create a simple ToDo List to compare traditional API usage in the Pages Router with Server Actions in the App Router.
      

      Pages Router - API

      First, we create a /api/todos endpoint:
      // app/api/todos/route.js
      import { NextResponse } from 'next/server'
      
      const data = ['Reading', 'Writing', 'Meditation']
      
      export async function GET() {
      return NextResponse.json({ data })
      }
      
      export async function POST(request) {
      const formData = await request.formData()
      const todo = formData.get('todo')
      data.push(todo)
      return NextResponse.json({ data })
      }
      Visiting /api/todos gives you a list of todos.
      
      Next, create a form page:
      // pages/form.js
      import { useEffect, useState } from "react"
      
      export default function Page() {
      const [todos, setTodos] = useState([])
      
      useEffect(() => {
      const fetchData = async () => {
      const { data } = await (await fetch('/api/todos')).json()
      setTodos(data)
      }
      fetchData()
      }, [])
      
      async function onSubmit(event) {
      event.preventDefault()
      const response = await fetch('/api/todos', {
      method: 'POST',
      body: new FormData(event.currentTarget),
      })
      const {data} = await response.json()
      setTodos(data)
      }
      
      return (
      <>
      <form onSubmit={onSubmit}>
      <input type="text" name="todo" />
      <button type="submit">Submit</button>
      </form>
      <ul>
      {todos.map((todo, i) => <li key={i}>{todo}</li>)}
      </ul>
      </>
      )
      }
      This form submits data to /api/todos, which updates the list of todos.
      

      App Router - Server Actions

      Now, let’s implement the same ToDo List using Server Actions:
      // app/form2/page.js
      import { findToDos, createToDo } from './actions';
      
      export default async function Page() {
      const todos = await findToDos();
      return (
      <div className="max-w-sm mx-auto mt-8 p-4 bg-white shadow-md rounded-lg">
      <h2 className="text-xl font-bold mb-4">Enter Task</h2>
      <form action={createToDo} className="mb-4">
      <input
      type="text"
      name="todo"
      className="w-full p-2 mb-2 border border-gray-300 rounded"
      placeholder="Enter a new task"
      />
      <button
      type="submit"
      className="w-full bg-black text-white py-2 rounded hover:bg-gray-800"
      >
      Add
      </button>
      </form>
      <ul className="space-y-2">
      {todos.map((todo, i) => (
      <li key={i} className="flex justify-between items-center">
      <span>{todo}</span>
      <button className="bg-black text-white px-4 py-1 rounded text-sm hover:bg-gray-800">
      Delete
      </button>
      </li>
      ))}
      </ul>
      </div>
      )
      }
      Here’s the actions.js file:
      'use server'
      
      import { revalidatePath } from "next/cache";
      
      const data = ['Reading', 'Writing', 'Meditation']
      
      export async function findToDos() {
      return data
      }
      
      export async function createToDo(formData) {
      const todo = formData.get('todo')
      data.push(todo)
      revalidatePath("/form2");
      return data
      }
      
      The effect is as follows:
      

      Server Actions Explained


      

      Basic Principle

      Server Actions are implemented by sending a POST request to the current page’s URL when a form is submitted. Next.js automatically inserts a hidden <input> with a value like $ACTION_ID_xxxxxxxx, which helps identify the correct action on the server.
      
      Upon form submission, the server processes the form data, triggers the relevant action, and returns the updated RSC Payload, which is used to update the UI with the latest data.
      Sending a POST request:
      
      Payload with $ACTION_ID
      
      Returns the RSC Payload, which is used to render the updated data
      
      In short:
      1. Server Actions utilize the POST request method internally, accessing the current page address and differentiating based on $ACTION_ID.
      2. Integration of Server Actions with Next.js caching and revalidation architecture enables Next.js to provide updated UI and new data simultaneously when an Action is called.
      

      Benefits of Using Server Actions

      • Cleaner Code: No need to manually create API endpoints. Since Server Actions are just functions, they can be reused throughout your application.
      • Progressive Enhancement: Even with JavaScript disabled, forms using Server Actions will still work by reloading the page upon submission.

      Key Considerations

      • Serialization: Server Actions' arguments and return values must be serializable (i.e., JSON.stringify should work without errors).
      • Inheritance: Server Actions inherit runtime and configuration options from the page or layout where they are used, including fields like maxDuration.

      Advanced Usage | Triggering Actions in Event Handlers


      
      You can trigger Server Actions from event handlers. For example, let’s add a "Add Exercise" button to our ToDo List:
      // app/form2/page.js
      import { findToDos, createToDo } from './actions';
      import Button from './button';
      
      export default async function Page() {
      const todos = await findToDos();
      return (
      <div className="max-w-sm mx-auto mt-8 p-4 bg-white shadow-md rounded-lg">
      <h2 className="text-xl font-bold mb-4">Enter Task</h2>
      <form action={createToDo} className="mb-4">
      <div className="flex space-x-2">
      <input
      type="text"
      name="todo"
      className="flex-grow p-2 border border-gray-300 rounded"
      placeholder="Enter a new task"
      />
      <Button type="submit" className="px-4">
      Add
      </Button>
      </div>
      </form>
      <Button className="w-full mb-2 bg-black text-white py-2 rounded hover:bg-gray-800">
      Add Exercise
      </Button>
      <ul className="space-y-2">
      {todos.map((todo, i) => (
      <li key={i} className="flex justify-between items-center">
      <span>{todo}</span>
      <button className="bg-black text-white px-4 py-1 rounded text-sm hover:bg-gray-800">
      Delete
      </button>
      </li>
      ))}
      </ul>
      </div>
      )
      }
      The button component:
      // app/form2/button.js
      'use client'
      import { createToDoDirectly } from './actions';
      export default function Button({ children, className }) {
      return (
      <button
      onClick={async () => {
      const data = await createToDoDirectly('Exercise')
      alert(JSON.stringify(data))
      }}
      className={`bg-black text-white py-2 rounded hover:bg-gray-800 ${className}`}
      >
      {children}
      </button>
      );
      }
      Add the new action to actions.js:
      export async function createToDoDirectly(value) {
      const form = new FormData()
      form.append("todo", value);
      return createToDo(form)
      }
      The effect is as follows:
      

      Conclusion


      
      By now, you should have a solid understanding of how to use Server Actions in Next.js. Server Actions have become a key feature in Next.js since version 14, and mastering them is essential for building full-stack applications in Next.js.
      In future lessons, we’ll dive deeper into Server Actions, covering topics like handling form submission states, server-side validation, optimistic updates, error handling, accessing cookies and headers, and implementing redirects.