useOptimistic (React 19) ist der Hook für optimistische UI-Updates. Statt zu warten, bis der Server eine Aktion bestätigt, zeigt die UI sofort den erwarteten neuen Zustand — der Like-Button füllt sich sofort, der Kommentar erscheint sofort in der Liste. Wenn der Server tatsächlich erfolgreich antwortet, ist die UI bereits korrekt. Wenn er fehlschlägt, fällt die UI automatisch auf den echten State zurück. Das macht Apps wahrnehmbar schneller, ohne dass der Code mit komplexer Rollback-Logik verschmutzt wird. useOptimistic arbeitet besonders gut mit Server Actions und Concurrent-Features.

Signatur

TypeScript signatur.ts
const [optimisticState, addOptimistic] = useOptimistic(
    actualState,
    (currentState, optimisticValue) => newState
);
  • actualState: der „echte" State (z.B. aus Server-Daten, useState, useReducer).
  • updateFn: bekommt aktuellen optimistischen State + den optimistischen Update-Wert, liefert neuen optimistischen State.

Rückgabe: ein Tupel aus dem optimistischen State (für UI-Rendering) und einer addOptimistic-Funktion (zum Auslösen eines optimistischen Updates).

Klassisches Beispiel — Kommentar hinzufügen

TypeScript CommentList.jsx
import { useOptimistic, useState } from 'react';

export default function CommentList({ initialComments }) {
    const [comments, setComments] = useState(initialComments);

    const [optimisticComments, addOptimisticComment] = useOptimistic(
        comments,
        (state, newComment) => [...state, { ...newComment, pending: true }]
    );

    async function handleSubmit(formData) {
        const text = formData.get('text');
        const newComment = { id: crypto.randomUUID(), text };

        // 1. Optimistisch hinzufügen — UI zeigt sofort
        addOptimisticComment(newComment);

        // 2. Server-Call
        const saved = await saveCommentToServer(newComment);

        // 3. Echten State aktualisieren — useOptimistic ersetzt
        //    automatisch durch den neuen actualState
        setComments(prev => [...prev, saved]);
    }

    return (
        <>
            <ul>
                {optimisticComments.map(c => (
                    <li key={c.id} style={{ opacity: c.pending ? 0.5 : 1 }}>
                        {c.text} {c.pending && '(sende…)'}
                    </li>
                ))}
            </ul>
            <form action={handleSubmit}>
                <input name="text" />
                <button type="submit">Senden</button>
            </form>
        </>
    );
}

User sieht den Kommentar sofort, mit halber Opacity und „sende…"-Hinweis. Wenn der Server antwortet, wird comments aktualisiert — der optimistische Wert wird automatisch durch den echten ersetzt.

Rollback bei Fehler

Wenn der Server fehlschlägt, fällt useOptimistic automatisch auf den actualState zurück, sobald die umgebende Action beendet ist.

TypeScript MitFehler.jsx
async function handleSubmit(formData) {
    const newComment = { id: crypto.randomUUID(), text: formData.get('text') };

    addOptimisticComment(newComment);

    try {
        const saved = await saveCommentToServer(newComment);
        setComments(prev => [...prev, saved]);
    } catch (e) {
        // optimisticComments fällt automatisch auf comments zurück
        showError('Konnte nicht senden: ' + e.message);
    }
}

Kein manueller Rollback nötig — React verwirft den optimistischen Wert beim nächsten Render, weil comments nie aktualisiert wurde.

Use-Cases

Klassische Anwendungsfälle:

  • Like/Heart-Button: Symbol sofort gefüllt anzeigen.
  • Kommentar/Post abschicken: Eintrag sofort in der Liste sichtbar.
  • Todo-Status togglen: Häkchen sofort umgeklickt.
  • Drag-and-Drop: Reihenfolge sofort sichtbar.
  • Counter-Inkrement: Wert sofort höher.

Nicht-Use-Cases:

  • Form-Validation: Validität ist sofort bekannt, nicht „optimistisch".
  • Daten lesen: useOptimistic ist für Schreib-Operationen.
  • Lang-laufende Background-Jobs: optimistisch macht keinen Sinn, wenn der User auf das Ergebnis warten muss.

In Kombination mit Server Actions

useOptimistic arbeitet besonders gut mit Server Actions (React 19 / Next.js App Router). Server Actions sind async-Funktionen, die direkt vom Client aus auf dem Server laufen — useOptimistic übernimmt den UI-Sync.

TypeScript ServerAction.jsx
'use client';

import { useOptimistic } from 'react';
import { likePost } from './actions';   // Server Action

export default function LikeButton({ post }) {
    const [optimisticLikes, addOptimisticLike] = useOptimistic(
        post.likes,
        (current) => current + 1
    );

    return (
        <form action={async () => {
            addOptimisticLike();
            await likePost(post.id);
        }}>
            <button type="submit">❤️ {optimisticLikes}</button>
        </form>
    );
}

Pending-Markierung im optimistischen Wert

Best Practice: den optimistischen Eintrag visuell als „in Bearbeitung" kennzeichnen (Opacity, Spinner, Label).

TypeScript PendingFlag.jsx
const [optimistic, addOpt] = useOptimistic(
    data,
    (state, value) => [
        ...state,
        { ...value, _optimistic: true }
    ]
);

// In der UI
{optimistic.map(item => (
    <Card
        key={item.id}
        style={{ opacity: item._optimistic ? 0.5 : 1 }}
        {...item}
    />
))}

Der User sieht, dass etwas „im Flug" ist — kein blindes Vertrauen, dass es geklappt hat.

Besonderheiten

useOptimistic ist React 19+ — vorher manuell mit `useState`.

Pre-19 mussten optimistische Updates mit useState + Rollback-Logik manuell gebaut werden. useOptimistic übernimmt den Rollback automatisch und integriert sich in Concurrent-Rendering.

addOptimistic feuert pro Action — nicht eigenständig.

Der optimistische Wert wirkt nur während einer laufenden async-Action (z.B. einer Server-Action oder eines async-Event-Handlers). Sobald die Action endet, fällt useOptimistic auf den actualState zurück — auch wenn dieser sich nicht geändert hat.

Rollback ist automatisch — kein manuelles Cleanup nötig.

Wenn die Action mit Error endet ODER der actualState nie aktualisiert wird, kommt der optimistische Wert weg. Kein explizites setOptimistic(originalValue) nötig.

Update-Funktion bekommt aktuellen State + Optimistic-Value.

(state, optimisticValue) => newState — wichtig: state ist der AKTUELLE optimistische State (kann sich durch frühere addOptimistic-Aufrufe ändern), nicht actualState.

Mehrere parallele optimistische Updates — funktional gemerkt.

Wenn mehrere addOptimistic-Aufrufe in Folge passieren (z.B. drei Likes hintereinander), wird die Update-Funktion mehrmals mit dem akkumulierten State aufgerufen. Daher: Update-Funktion muss korrekt zusammensetzbar sein.

Pending-Flag im Update-Wert ist Best Practice.

Markiere optimistische Einträge sichtbar (Opacity, Spinner). User soll wissen: „das ist noch nicht final". Vermeidet Verwirrung bei Server-Fehlern.

useOptimistic + Server Actions = der React-19-Workflow.

Server Actions liefern den actualState-Update, useOptimistic übernimmt das sofortige Sichtbarmachen. Zusammen mit useFormStatus und useActionState der vollständige Forms-Stack in modernem React.

Nicht für reine Lese-Operationen — nur für mutierende Actions.

useOptimistic ergibt nur Sinn, wenn eine Aktion den Server-State ändert und das Ergebnis vorhersagbar ist. Bei reinen GET-Requests gibt's kein „optimistisch" — entweder Daten sind da oder Loading-State.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Hooks

Zur Übersicht