Retour au blog

Next.js 15 : Server Actions et mutations en douceur

15 février 20252 min de lecture
Next.jsReactServer Actions

Introduction

Next.js 15 apporte une maturité accrue aux **Server Actions**. On peut désormais gérer des mutations directement depuis des composants serveur ou des formulaires, avec une DX soignée.

Pourquoi les Server Actions ?

  • **Moins de boilerplate** : plus besoin d’exposer une route API pour chaque mutation.
  • **Typage de bout en bout** : les arguments et le retour sont typés.
  • **Progressive enhancement** : les formulaires fonctionnent même sans JavaScript.

Exemple minimal

// app/actions.ts
"use server"
import { revalidatePath } from "next/cache"

export async function createPost(formData: FormData) {
  const title = formData.get("title") as string
  if (!title) return

  await db.posts.create({ title })
  revalidatePath("/blog")
}

Et côté interface (App Router) :

// app/blog/new-post/page.tsx
import { createPost } from "../actions"

export default function NewPostPage() {
  return (
    <form action={createPost} className="space-y-4">
      <label className="block text-sm font-medium">
        Titre
        <input
          name="title"
          className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm"
        />
      </label>
      <button
        type="submit"
        className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
      >
        Créer l’article
      </button>
    </form>
  )
}

Ce qui change vraiment avec la 15

Deux choses changent concrètement le quotidien par rapport aux versions précédentes, pas juste la stabilisation de l’API :

  • **Le cache `fetch` n’est plus activé par défaut.** Avant, un `fetch` dans un Server Component était mis en cache sauf mention contraire ; depuis la 15, c’est l’inverse. Ça change la façon de penser une Server Action : on n’a plus besoin de se méfier d’un cache silencieux qui renverrait des données périmées après une mutation, mais il faut désormais opt-in explicitement (`{ cache: "force-cache" }`) là où on veut vraiment du cache.
  • **`useActionState`** (l’évolution de `useFormState`, maintenant fourni par React lui-même) simplifie le suivi d’état d’une Server Action depuis un Client Component :
"use client"
import { useActionState } from "react"
import { createPost } from "../actions"

export function PostForm() {
  const [state, formAction, isPending] = useActionState(createPost, null)

  return (
    <form action={formAction} className="space-y-3">
      <input name="title" className="rounded-md border border-border px-3 py-2 text-sm" />
      <button disabled={isPending} className="rounded-md bg-primary px-3 py-1.5 text-sm text-primary-foreground">
        {isPending ? "Envoi…" : "Créer"}
      </button>
      {state?.error && <p className="text-sm text-destructive">{state.error}</p>}
    </form>
  )
}

Plus besoin de jongler avec un `useState` séparé pour le pending et l’erreur : la Server Action et l’état du formulaire sont liés nativement.

Bonnes pratiques

1. **Toujours valider les données** (Zod, valibot, etc.) avant d’écrire en base — une Server Action reste un point d’entrée public, au même titre qu’une route API. 2. **Isoler la logique métier** dans des fonctions réutilisables pour éviter que les Server Actions grossissent trop. 3. Utiliser `revalidatePath` ou `revalidateTag` juste après une mutation pour garder l’UI synchronisée, en gardant en tête que le comportement de cache par défaut a changé en 15.

Conclusion

Les Server Actions sont un outil central pour des applications Next.js modernes. La version 15 ne change pas leur fonctionnement de base, mais le nouveau comportement de cache et `useActionState` changent la façon de les écrire au quotidien.