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 II

      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

      When developing Server Actions in Next.js, there are several critical aspects to keep in mind to ensure your code is both functional and maintainable. Here are the main points to focus on:

      1. Retrieving Data

      When working with Server Actions, the way you retrieve data depends on how the action is invoked:
      • Form Actions: If using a form action, the Server Action function receives formData as its first parameter:
      export default function Page() {
      async function createInvoice(formData) {
      'use server'
      const rawFormData = {
      customerId: formData.get('customerId')
      }
      // mutate data
      // revalidate cache
      }
      return <form action={createInvoice}>...</form>
      }
      • Form Action with useFormState: If using a form action with useFormState, the Server Action function receives prevState as the first parameter and formData as the second:
      '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>
      )
      }
      • Direct Invocation: If you directly invoke the Server Action, the parameters depend on how the function is called. For example:
      'use client'
      
      import { createToDoDirectly } from './actions';
      
      export default function Button({ children }) {
      return (
      <button onClick={async () => {
      const data = await createToDoDirectly('Workout');
      alert(JSON.stringify(data));
      }}>
      {children}
      </button>
      );
      }
      'use server'
      
      export async function createToDoDirectly(value) {
      const form = new FormData();
      form.append("todo", value);
      return createToDo(form);
      }
      

      2. Form Validation

      Next.js recommends using HTML's native validation attributes (e.g., required, type="email") for basic form validation.
      For more advanced server-side validation, you can use schema validation libraries like zod:
      'use server'
      
      import { z } from 'zod'
      
      const schema = z.object({
      email: z.string().email("Invalid Email"),
      })
      
      export default async function createsUser(formData) {
      const validatedFields = schema.safeParse({
      email: formData.get('email'),
      })
      
      if (!validatedFields.success) {
      return {
      errors: validatedFields.error.flatten().fieldErrors,
      };
      }
      
      // Mutate data
      }
      

      3. Revalidating Data

      After mutating data with Server Actions, it's crucial to revalidate the relevant data to ensure the UI reflects the latest state. You can use revalidatePath or revalidateTag:
      • Using revalidatePath:
      'use server'
      
      import { revalidatePath } from 'next/cache'
      
      export async function createPost() {
      try {
      // Mutate data
      } catch (error) {
      // Handle error
      }
      
      revalidatePath('/posts');
      }
      • Using revalidateTag:
      'use server'
      
      import { revalidateTag } from 'next/cache'
      
      export async function createPost() {
      try {
      // Mutate data
      } catch (error) {
      // Handle error
      }
      
      revalidateTag('posts');
      }

      4. Error Handling

      Handling errors in Server Actions is essential for providing a smooth user experience. There are two main approaches:
      • Returning Error Messages: If an operation fails, you can return an error message that can be displayed on the client:
      'use server'
      // app/actions.js
      export async function createTodo(prevState, formData) {
      try {
      await createItem(formData.get('todo'));
      return revalidatePath('/');
      } catch (e) {
      return { message: 'Failed to create' };
      }
      }
      On the client side, display the error message:
      'use client'
      // app/add-form.jsx
      import { useFormState, useFormStatus } from 'react-dom';
      import { createTodo } from '@/app/actions';
      
      const initialState = {
      message: null,
      };
      
      function SubmitButton() {
      const { pending } = useFormStatus();
      
      return (
      <button type="submit" aria-disabled={pending}>
      Add
      </button>
      );
      }
      
      export function AddForm() {
      const [state, formAction] = useFormState(createTodo, initialState);
      
      return (
      <form action={formAction}>
      <label htmlFor="todo">Enter Task</label>
      <input type="text" id="todo" name="todo" required />
      <SubmitButton />
      <p aria-live="polite" className="sr-only">
      {state?.message}
      </p>
      </form>
      );
      }
      • Throwing Errors: Alternatively, you can throw errors that will be caught by the nearest error.js file in the component tree:
      'use client'
      // error.js
      export default function Error() {
      return (
      <h2>Error Occurred</h2>
      );
      }
      
      // page.js
      import { useFormState } from 'react-dom';
      
      function AddForm() {
      async function serverActionWithError() {
      'use server';
      throw new Error(`This is an error in the Server Action`);
      }
      
      return (
      <form action={serverActionWithError}>
      <button type="submit">Submit</button>
      </form>
      );
      }
      
      export default AddForm;
      When the Server Action encounters an error, the error UI will be displayed.

      Conclusion

      Server Actions in Next.js are powerful tools for managing data mutations, but they require careful handling of form data, validation, revalidation, and error management. By following these guidelines, you can create robust and user-friendly applications that make full use of Server Actions.