CodShot
TSX Public

Optimistic Todo List with useOptimistic

Show a new todo immediately while the asynchronous save operation completes in the background.

#react #hooks #state #optimistic-ui
TSX
'use client';

import { startTransition, useOptimistic, useState } from 'react';

type Todo = {
  id: string;
  text: string;
  pending?: boolean;
};

type Props = {
  initialTodos: Todo[];
  createTodo: (text: string) => Promise<Todo>;
};

export function TodoList({ initialTodos, createTodo }: Props) {
  const [todos, setTodos] = useState(initialTodos);

  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (currentTodos, text: string) => [
      ...currentTodos,
      {
        id: crypto.randomUUID(),
        text,
        pending: true,
      },
    ],
  );

  function handleSubmit(formData: FormData) {
    const text = String(formData.get('text') ?? '').trim();

    if (!text) return;

    startTransition(async () => {
      addOptimisticTodo(text);

      const savedTodo = await createTodo(text);
      setTodos((current) => [...current, savedTodo]);
    });
  }

  return (
    <>
      <form action={handleSubmit}>
        <input name="text" placeholder="Add a todo" required />
        <button type="submit">Add</button>
      </form>

      <ul>
        {optimisticTodos.map((todo) => (
          <li key={todo.id} aria-busy={todo.pending}>
            {todo.text}
            {todo.pending ? ' (saving…)' : ''}
          </li>
        ))}
      </ul>
    </>
  );
}

Snippet actions

CodShot user
CodShot user
Updated: 2026-08-06 18:36
Public snippets
0
Forks
CodShot AI Help
Ask about CodShot features, plans, workspace, AI limits, referrals, and public help topics.
Hi! I can help you understand how CodShot works. What would you like to know?
For account-specific or sensitive issues, please open a support ticket. Open support