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
      Form

      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

      Introduction


      
      In the previous article, we covered the basic usage of Server Actions in Next.js. This time, we will explore the "standard" usage of Server Actions, such as which APIs and libraries are often paired with them and what you should consider when writing a Server Action.
      We will also address common issues developers face when working with Server Actions, including how to implement optimistic updates, handle errors, access Cookies and Headers, and manage redirects.

      Working with Forms


      
      Let’s start with how Server Actions handle form submissions and the APIs that are commonly used in this context.

      1.useFormStatus

      The useFormStatus hook is a React hook that returns the status of a form submission. Here’s how it can be used:
      'use client'
      // app/submit-button.jsx
      import { useFormStatus } from 'react-dom'
      
      export function SubmitButton() {
      const { pending } = useFormStatus()
      
      return (
      <button type="submit" aria-disabled={pending}>
      {pending ? 'Adding' : 'Add'}
      </button>
      )
      }
      // app/page.jsx
      import { SubmitButton } from '@/app/submit-button'
      
      export default async function Home() {
      return (
      <form action={...}>
      <input type="text" name="field-name" />
      <SubmitButton />
      </form>
      )
      }
      Important: useFormStatus must be used within a component that is a descendant of a <form> element, like in the example above. It’s crucial not to use it directly within the form component as this would not work:
      function Form() {
      // 🚩 `pending` will never be true
      // useFormStatus does not track the form rendered in this component
      const { pending } = useFormStatus();
      return <form action={submit}></form>;
      }

      2.useFormState

      useFormState is another React hook that updates state based on the outcome of the form action. Here's how it can be used:
      For a simple React example:
      import { useFormState } from "react-dom";
      
      async function increment(previousState, formData) {
      return previousState + 1;
      }
      
      function StatefulForm() {
      const [state, formAction] = useFormState(increment, 0);
      return (
      <form>
      {state}
      <button formAction={formAction}>Increment</button>
      </form>
      )
      }
      In Next.js, combining it with Server Actions:
      'use client'
      
      import { useFormState } from 'react-dom'
      
      export default function Home() {
      
      async function createTodo(prevState, formData) {
      return prevState.concat(formData.get('todo'));
      }
      
      const [state, formAction] = useFormState(createTodo, [])
      
      return (
      <form action={formAction}>
      <input type="text" name="todo" />
      <button type="submit">Submit</button>
      <p>{state.join(',')}</p>
      </form>
      )
      }

      Hands-On Example

      Let's combine useFormStatus and useFormState to handle form submissions using Server Actions. The project structure is as follows:
      cssCopy codeapp
      └─ form3
      ├─ actions.js
      ├─ form.js
      └─ page.js

      app/form3/page.js

      import { findToDos } from './actions';
      import AddToDoForm from './form';
      
      export default async function Page() {
      const todos = await findToDos();
      return (
      <div className="max-w-md mx-auto mt-8 p-6 bg-white rounded-lg shadow-md">
      <h1 className="text-2xl font-bold mb-4">To-do List</h1>
      <AddToDoForm />
      <ul className="mt-4 space-y-2">
      {todos.map((todo, i) => (
      <li key={i} className="bg-gray-100 p-2 rounded-md">{todo}</li>
      ))}
      </ul>
      </div>
      )
      }

      app/form3/form.js

      'use client'
      
      import { useFormState, useFormStatus } from 'react-dom'
      import { createToDo } from './actions';
      
      const initialState = {
      message: '',
      }
      
      function SubmitButton() {
      const { pending } = useFormStatus()
      return (
      <button
      type="submit"
      aria-disabled={pending}
      className="bg-black text-white px-4 py-2 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 disabled:opacity-50"
      disabled={pending}
      >
      {pending ? 'Adding...' : 'Add'}
      </button>
      )
      }
      
      export default function AddToDoForm() {
      const [state, formAction] = useFormState(createToDo, initialState)
      
      return (
      <form action={formAction} className="space-y-4">
      <div className="flex space-x-2">
      <input
      type="text"
      name="todo"
      className="flex-grow px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
      placeholder="Input to-do item.."
      />
      <SubmitButton />
      </div>
      <p aria-live="polite" className="sr-only">
      {state?.message}
      </p>
      </form>
      )
      }

      app/form3/actions.js

      'use server'
      
      import { revalidatePath } from "next/cache";
      
      const sleep = ms => new Promise(r => setTimeout(r, ms));
      
      let data = ['Reading', 'Writing', 'Meditation']
      
      export async function findToDos() {
      return data
      }
      
      export async function createToDo(prevState, formData) {
      await sleep(500)
      const todo = formData.get('todo')
      data.push(todo)
      revalidatePath("/form3");
      return {
      message: `Added ${todo} successfully!`
      }
      }
      The interaction works as follows:
      
      • useFormState: The first parameter in the Server Action function is prevState, and the second is formData.
      • useFormStatus: It should be used in a separate component under the form, such as the submit button component.
      Additionally, the line:
      <p aria-live="polite" className="sr-only">
      {state?.message}
      </p>
      
      • aria-live: It’s an ARIA attribute used to politely notify users of changes.
      • sr-only: The content is meant for screen readers only, ensuring accessibility.
      You should also include the following CSS to hide it visually but keep it accessible to screen readers:
      .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border-width: 0;
      }
      

      Conclusion

      This article introduced advanced and "standard" usage of Server Actions, focusing on integrating with form submissions using hooks like useFormStatus and useFormState. These tools are essential for handling form state and submission status effectively in a Next.js application.
      In the next part, we will dive deeper into more complex scenarios, including optimistic updates, error handling, accessing Cookies and Headers, and managing redirects with Server Actions.