0
0
NextJSframework~5 mins

Optimistic updates pattern in NextJS

Choose your learning style9 modes available
Introduction

Optimistic updates make your app feel faster by showing changes right away before the server confirms them.

When you want to update a list item immediately after a user edits it, without waiting for the server.
When a user likes or votes on something and you want to show the new count instantly.
When submitting a form and you want to show the new data right away to keep the user engaged.
When deleting an item and you want to remove it from the UI immediately for a smooth experience.
Syntax
NextJS
const [data, setData] = useState(initialData);

async function updateData(newData) {
  const previousData = data;
  // 1. Update UI immediately
  setData(newData);

  try {
    // 2. Send update to server
    await fetch('/api/update', { method: 'POST', body: JSON.stringify(newData), headers: { 'Content-Type': 'application/json' } });
  } catch (error) {
    // 3. Revert UI if server fails
    setData(previousData);
  }
}

Update the UI state first, then send the server request.

If the server fails, revert the UI to the old state to keep data correct.

Examples
Instantly increase like count, then send to server. If server fails, go back.
NextJS
const [count, setCount] = useState(0);

function like() {
  const previousCount = count;
  setCount(previousCount + 1); // update UI immediately
  fetch('/api/like', { method: 'POST' }).catch(() => setCount(previousCount)); // revert if error
}
Add a new item to the list immediately, then confirm with server.
NextJS
const [items, setItems] = useState(['apple', 'banana']);

function addItem(newItem) {
  const previousItems = items;
  setItems([...previousItems, newItem]); // show new item right away
  fetch('/api/add', { method: 'POST', body: JSON.stringify(newItem), headers: { 'Content-Type': 'application/json' } })
    .catch(() => setItems(previousItems)); // undo if error
}
Sample Program

This Next.js component lets you add todos instantly. It updates the list right away, sends the new todo to the server, and if the server fails, it removes the new todo and shows an alert.

It uses accessible labels and keyboard support for better user experience.

NextJS
import { useState } from 'react';

export default function OptimisticUpdate() {
  const [todos, setTodos] = useState(['Learn Next.js', 'Build app']);
  const [input, setInput] = useState('');

  async function addTodo() {
    if (!input.trim()) return;

    const previousTodos = todos;
    const newTodos = [...todos, input];
    setTodos(newTodos); // update UI immediately

    try {
      const res = await fetch('/api/todos', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ todo: input })
      });

      if (!res.ok) throw new Error('Failed to save');

      setInput(''); // clear input on success
    } catch {
      setTodos(previousTodos); // revert UI if error
      alert('Failed to add todo. Please try again.');
    }
  }

  return (
    <main>
      <h1>My Todos</h1>
      <ul>
        {todos.map((todo, i) => <li key={i}>{todo}</li>)}
      </ul>
      <input
        aria-label="New todo"
        value={input}
        onChange={e => setInput(e.target.value)}
        onKeyDown={e => e.key === 'Enter' && addTodo()}
      />
      <button onClick={addTodo} aria-label="Add todo">Add</button>
    </main>
  );
}
OutputSuccess
Important Notes

Always handle errors to avoid showing wrong data.

Optimistic updates improve user experience but need careful rollback logic.

Use accessible labels and keyboard events for better usability.

Summary

Optimistic updates show changes immediately to make apps feel faster.

Update UI first, then confirm with server, and revert if needed.

Good error handling and accessibility improve the pattern's success.