Mutating Data

The Next.js team is working on a new RFC for mutating data in Next.js. This RFC has not been published yet. For now, we recommend the following pattern:

You can mutate data inside the app directory with router.refresh().

Example

Let's consider a list view. Inside your Server Component, you fetch the list of items:

app/page.tsx
import Todo from './todo';

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

async function getTodos() {
  const res = await fetch('https://api.example.com/todos');
  const todos: Todo[] = await res.json();
  return todos;
}

export default async function Page() {
  const todos = await getTodos();
  return (
    <ul>
      {todos.map((todo) => (
        <Todo key={todo.id} {...todo} />
      ))}
    </ul>
  );
}

Each individual item has its own Client Component. This allows the component to use event handlers (like onClick or onSubmit) to trigger a mutation.

app/todo.tsx
"use client";

import { useRouter } from 'next/navigation';

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

async function update(id: number, completed: boolean, refresh: () => void) {
  await fetch(`https://api.example.com/todo/${id}`, {
    method: 'PUT',
    body: JSON.stringify({ completed }),
  });

  // Refresh the current route and fetch new data from the server
  refresh();
}

export default function Todo(todo: Todo) {
  const router = useRouter();

  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={(e) => update(todo.id, !todo.completed, router.refresh)}
      />
      {todo.title}
    </li>
  );
}

By calling refresh(), the current route will refresh and fetch an updated list of todos from the server. This does not affect browser history, but it does refresh data from the root layout down. When using refresh(), client-side state is not lost, including both React and browser state.