facebook/react · error · Error

Cannot update optimistic state while rendering.

Error message

Cannot update optimistic state while rendering.

What it means

dispatchOptimisticState backs the setter returned by useOptimistic. If called while the owning component is rendering (isRenderPhaseUpdate(fiber) true), it throws when the throwIfDuringRender flag is set — unlike startTransition-during-render, which only warns because it predates the rule. Optimistic updates are input signals from event handlers/actions; they may never originate during render.

Source

Thrown at packages/react-reconciler/src/ReactFiberHooks.js:3796

      : SyncLane;
  const update: Update<S, A> = {
    lane: lane,
    // After committing, the optimistic update is "reverted" using the same
    // lane as the transition it's associated with.
    revertLane: requestTransitionLane(transition),
    gesture: null,
    action,
    hasEagerState: false,
    eagerState: null,
    next: null as any,
  };

  if (isRenderPhaseUpdate(fiber)) {
    // When calling startTransition during render, this warns instead of
    // throwing because throwing would be a breaking change. setOptimisticState
    // is a new API so it's OK to throw.
    if (throwIfDuringRender) {
      throw new Error('Cannot update optimistic state while rendering.');
    } else {
      // startTransition was called during render. We don't need to do anything
      // besides warn here because the render phase update would be overidden by
      // the second update, anyway. We can remove this branch and make it throw
      // in a future release.
      if (__DEV__) {
        console.error('Cannot call startTransition while rendering.');
      }
    }
  } else {
    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
    if (root !== null) {
      // NOTE: The optimistic update implementation assumes that the transition
      // will never be attempted before the optimistic update. This currently
      // holds because the optimistic update is always synchronous. If we ever
      // change that, we'll need to account for this.
      startUpdateTimerByLane(lane, 'setOptimistic()', fiber);
      scheduleUpdateOnFiber(root, fiber, lane);

View on GitHub (pinned to eafeac097b)

Solutions

  1. Move the addOptimistic call into an event handler, form action, or transition callback
  2. If the goal is render-time behavior, render the merged view directly (compute base + pending during render) instead of dispatching
  3. Initialize the optimistic hook with the right fallback and only enqueue updates on user input

Example fix

// before
function List({todos}) {
  const [shown, addOptimistic] = useOptimistic(todos);
  if (todos.length === 0) addOptimistic(placeholder); // throws during render
  return <ul>{shown.map(...)}</ul>;
}

// after
function List({todos}) {
  const [shown, addOptimistic] = useOptimistic(todos, appendTodo);
  // addOptimistic is only called from onSubmit / actions / transitions
  return <ul>{shown.map(...)}</ul>;
}
Defensive patterns

Strategy: try-catch

Try / catch

// Render-phase throw: boundary around optimistic components
<ErrorBoundary fallback={<PlainList/>} onError={(e) => warnDeveloper('addOptimistic called during render', e)}>
  <OptimisticTodos />
</ErrorBoundary>

Prevention

When it happens

Trigger: Calling addOptimistic(...) (the useOptimistic setter) in the component body, in a JSX expression or default parameter evaluated during render, or from any code that runs while React is rendering that component.

Common situations: Trying to derive optimistic state during render; converting a state toggle into useOptimistic without moving the dispatch out of render; optimistic UI code shared between render and handlers.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/ba9414ae7875da0a. Report an issue: GitHub.