Skip to content
← Journal
33 min readAta Mohammadi

Staff-Level React Architecture and Modern Refactoring: A Playbook for React 19.x

A senior engineer's field guide to React 19.3: useActionState, useOptimistic, the React Compiler, ViewTransition, Fragment refs, and how to review async React code the way a staff engineer would.

The architectural evolution of React from version 18 through 19.3 is a shift from client-orchestrated, imperative state synchronization toward declarative, transition-aware concurrency and unified client-server execution. In the old model, teams treated asynchronous interactions as secondary effects, coordinating loading spinners, error boundaries, and race conditions through ad-hoc combinations of local state flags and lifecycle hooks. Modern React formalizes async operations inside concurrent transitions, giving you native abstractions for background execution, optimistic projections, resource consumption, and DOM interaction.

For senior and staff engineers, reviewing frontend code now means moving past historical idioms — manual memoization chains, multi-flag async state machines, lifecycle-driven synchronization — and checking whether a diff uses the primitives the runtime was built around. React 19 treats transitions as the baseline unit of state transformation. Actions — async functions executed inside transitions — give you pending states, sequential queueing, automatic form resets, and unified error propagation for free. The minor releases build on that: 19.2 adds non-reactive effect boundaries via useEffectEvent and prerendering controls, and 19.3 stabilizes declarative <ViewTransition>, Fragment DOM refs, and deterministic server-rendering bailouts via browser().

The primitives at a glance

Hook / API Architectural role Replaces Core guarantee
useActionState Form and action state lifecycle useState flag soup, useFormState Runs reducers inside concurrent transitions, tracks isPending, queues dispatches sequentially
useOptimistic Ephemeral optimistic projections Manual rollback via a useRef snapshot Binds to an ongoing transition, computes optimistic state while pending, reverts automatically on settle
useTransition Non-blocking async orchestration startTransition (sync-only in React 18) Supports async/await natively; keeps the UI responsive during heavy work
use(Resource) Resource and context unwrapping useContext, custom promise-resolving effects Unwraps promises via Suspense; callable conditionally, inside if and loops
useFormStatus Hierarchical form submission status Prop-drilled status or local context Reads pending, data, method, action of the nearest parent <form>
useEffectEvent Non-reactive effect logic Omitted deps, useRef trampolines Reads latest props/state inside an effect without re-triggering it
useRef (19.x) Mutable reference and DOM target forwardRef wrapping Accepted directly as a prop; callback refs support cleanup functions
<ViewTransition> Declarative view transition coordination Manual document.startViewTransition Syncs DOM transitions with concurrent commits and Suspense
Fragment refs Wrapperless multi-node DOM interactivity ReactDOM.findDOMNode(), wrapper <div>s A FragmentInstance exposes event dispatch, visibility, and focus across sibling nodes
browser() Deterministic client-only bailout typeof window !== 'undefined' workarounds Throws a sentinel during SSR to bail to the nearest Suspense boundary

1. useActionState: concurrent form and state action lifecycle

useActionState manages state transitions driven by async operations (Actions). It accepts an action reducer, an initial state, and an optional permalink, and returns a tuple of the current state, a dispatch function (suitable for <form action={...}> or manual invocation), and an isPending flag:

ActionState(n+1) = Reducer(ActionState(n), Payload)

Unlike a plain useReducer, the reducer passed to useActionState can be async. When dispatched, React runs it inside a concurrent transition, toggles isPending, and sequences concurrent invocations one after another — eliminating race conditions between overlapping submissions. If the action completes without throwing, React treats it as successful and resets uncontrolled fields in the host <form> automatically.

import React, { useActionState } from 'react';

type State =
  | { status: 'idle' }
  | { status: 'success'; message: string }
  | { status: 'error'; message: string; submittedEmail?: string };

async function subscribeNewsletterAction(
  prevState: State,
  formData: FormData
): Promise<State> {
  const email = formData.get('email')?.toString().trim();

  if (!email || !email.includes('@')) {
    return {
      status: 'error',
      message: 'Please provide a valid corporate email address.',
      submittedEmail: email ?? '',
    };
  }

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

    if (!res.ok) {
      const errorPayload = await res.json().catch(() => ({}));
      return {
        status: 'error',
        message: errorPayload.message || 'Subscription failed on the server.',
        submittedEmail: email,
      };
    }

    return {
      status: 'success',
      message: 'Check your inbox to confirm your subscription.',
    };
  } catch (err) {
    return {
      status: 'error',
      message: err instanceof Error ? err.message : 'Network connection failure.',
      submittedEmail: email,
    };
  }
}

export function NewsletterSignup() {
  const [state, formAction, isPending] = useActionState<State, FormData>(
    subscribeNewsletterAction,
    { status: 'idle' }
  );

  return (
    <div className="newsletter-container">
      <h3>Subscribe to Engineering Insights</h3>

      {state.status === 'error' && (
        <p role="alert" className="error-banner">{state.message}</p>
      )}
      {state.status === 'success' && (
        <p role="status" className="success-banner">{state.message}</p>
      )}

      <form action={formAction}>
        <label htmlFor="newsletter-email">Work Email</label>
        <input
          id="newsletter-email"
          name="email"
          type="email"
          required
          disabled={isPending}
          defaultValue={state.status === 'error' ? state.submittedEmail : ''}
          placeholder="[email protected]"
        />
        <button type="submit" disabled={isPending} aria-busy={isPending}>
          {isPending ? 'Subscribing...' : 'Join Newsletter'}
        </button>
      </form>
    </div>
  );
}

Why does an uncontrolled <form> clear user input when an action returns a validation error, and how do you stop that? When a form Action completes without throwing, React treats the transition as committed and resets uncontrolled inputs — even if the returned state is a validation error, because the action itself didn't throw. To preserve what the user typed, return their submitted values as part of the error state and feed them back into defaultValue. React still resets the field, but it resets it to the value you just gave it.

How does useActionState handle rapid, repeated dispatches compared to a hand-rolled async event handler? A handler built on raw useState and async/await can resolve responses out of order. useActionState maintains an internal dispatch queue that runs sequentially inside concurrent transitions — each action receives the state the previous one returned. If any action throws, React cancels the queued actions and routes the exception to the nearest error boundary.

2. useOptimistic: ephemeral optimistic projections

useOptimistic computes an ephemeral view that's shown immediately during a pending transition:

UI State = ProjectFn(Committed State, Optimistic Update)

Calling the update function it returns applies a local projection that stays active only while the surrounding transition or Action is pending. Once the transition settles — commits or fails — the optimistic state is discarded automatically and the UI re-aligns with the committed state from the parent or server.

import React, { useOptimistic } from 'react';

export interface Comment {
  id: string;
  text: string;
  pending?: boolean;
}

interface CommentsListProps {
  comments: Comment[];
  onAddCommentAction: (formData: FormData) => Promise<void>;
}

export function CommentsList({ comments, onAddCommentAction }: CommentsListProps) {
  const [optimisticComments, addOptimisticComment] = useOptimistic(
    comments,
    (currentList: Comment[], newCommentText: string) => [
      ...currentList,
      {
        id: `temp-${Date.now()}`,
        text: newCommentText,
        pending: true,
      },
    ]
  );

  async function handleFormSubmit(formData: FormData) {
    const commentText = formData.get('comment')?.toString() || '';
    if (!commentText.trim()) return;

    // Apply immediate local update
    addOptimisticComment(commentText);

    // Await parent Action transition
    await onAddCommentAction(formData);
  }

  return (
    <section>
      <h3>Discussion ({optimisticComments.length})</h3>
      <ul className="comment-feed">
        {optimisticComments.map((comment) => (
          <li
            key={comment.id}
            style={{ opacity: comment.pending ? 0.6 : 1 }}
            aria-live={comment.pending ? 'polite' : 'off'}
          >
            {comment.text} {comment.pending && <em>(Sending...)</em>}
          </li>
        ))}
      </ul>

      <form action={handleFormSubmit}>
        <input
          name="comment"
          type="text"
          placeholder="Share your thoughts..."
          required
        />
        <button type="submit">Post Comment</button>
      </form>
    </section>
  );
}

What happens if useOptimistic's update function is called outside an active transition or Action? The update has no visible effect, or reverts instantly. useOptimistic relies on an active transition context — from startTransition, useTransition, or useActionState — to determine how long the optimistic value lives. Called outside one, React considers the work already finished and discards the projection in favor of the current committed state.

How does useOptimistic handle error rollback compared to manual ref-based caching? The manual version mutates local state and stashes the previous value in a useRef so a catch block can restore it — fragile, because an unhandled exception, an overlapping request, or a missing finally can leave state permanently wrong. useOptimistic never mutates persistent state; it computes an ephemeral view over the committed base. When the action rejects, the transition ends, React discards the projection, and the UI returns to the committed state with no manual cleanup.

3. useTransition: non-blocking concurrency

In React 19, useTransition natively supports async functions inside startTransition. Marking an update as a transition tells React it's non-urgent — urgent updates like keystrokes, cursor movement, and clicks can interrupt the transition's background rendering, keeping the interface responsive during heavy computation or data operations.

import React, { useState, useTransition } from 'react';

interface MetricRecord {
  id: number;
  timestamp: string;
  metric: number;
}

export function HighDensityDashboard({ fetchMoreMetrics }: {
  fetchMoreMetrics: (batchSize: number) => Promise<MetricRecord[]>
}) {
  const [data, setData] = useState<MetricRecord[]>([]);
  const [filter, setFilter] = useState('');
  const [isPending, startTransition] = useTransition();

  function handleLoadMore() {
    startTransition(async () => {
      // Async network boundary handled natively inside the transition
      const newBatch = await fetchMoreMetrics(5000);
      setData(prev => [...prev, ...newBatch]);
    });
  }

  // Synchronous urgent input update
  function handleFilterChange(e: React.ChangeEvent<HTMLInputElement>) {
    setFilter(e.target.value);
  }

  const filteredData = data.filter(item =>
    item.id.toString().includes(filter)
  );

  return (
    <div className="dashboard-layout">
      <div className="toolbar">
        <label htmlFor="filter-input">Filter ID:</label>
        <input
          id="filter-input"
          type="text"
          value={filter}
          onChange={handleFilterChange}
          placeholder="Filter immediately..."
        />

        <button onClick={handleLoadMore} disabled={isPending} aria-busy={isPending}>
          {isPending ? 'Processing Batch...' : 'Load 5,000 Data Points'}
        </button>
      </div>

      <div className="results-pane" style={{ opacity: isPending ? 0.7 : 1 }}>
        <p>Total Displayed: {filteredData.length}</p>
        <ul className="virtual-metrics">
          {filteredData.slice(0, 100).map(metric => (
            <li key={metric.id}>#{metric.id} - Value: {metric.metric.toFixed(4)}</li>
          ))}
        </ul>
      </div>
    </div>
  );
}

Why should a controlled text input's value never be updated inside startTransition? Transitions are non-urgent and interruptible. Wrapping a keystroke in one lets React defer flushing the new character to prioritize other work — perceptible typing lag, stutter, dropped keystrokes. Input state must always update synchronously; to defer the heavy work derived from that input, split it into an immediate input state and a deferred one, or reach for useDeferredValue.

4. Composite pattern: useActionState + useTransition + useOptimistic

Enterprise mutations typically need all three at once: orchestrated async execution with error handling (useActionState), non-blocking responsiveness during the surrounding UI change (useTransition), and immediate feedback with automatic rollback (useOptimistic).

import React, { useActionState, useOptimistic, useTransition } from 'react';

interface ProjectTicket {
  id: string;
  title: string;
  priority: 'LOW' | 'MEDIUM' | 'HIGH';
}

type ActionState = {
  tickets: ProjectTicket[];
  error: string | null;
};

interface KanbanColumnProps {
  initialTickets: ProjectTicket[];
  persistTicketUpdate: (ticketId: string, priority: ProjectTicket['priority']) => Promise<ProjectTicket[]>;
}

export function PriorityManager({ initialTickets, persistTicketUpdate }: KanbanColumnProps) {
  const [isTransitionPending, startTransition] = useTransition();

  // 1. Core Action lifecycle reducer
  const [state, formAction, isActionPending] = useActionState<ActionState, FormData>(
    async (prevState, formData) => {
      const ticketId = formData.get('ticketId') as string;
      const nextPriority = formData.get('priority') as ProjectTicket['priority'];

      try {
        const remoteTickets = await persistTicketUpdate(ticketId, nextPriority);
        return { tickets: remoteTickets, error: null };
      } catch (err) {
        return {
          tickets: prevState.tickets,
          error: err instanceof Error ? err.message : 'Database update failed',
        };
      }
    },
    { tickets: initialTickets, error: null }
  );

  // 2. Ephemeral optimistic projection
  const [optimisticTickets, setOptimisticTickets] = useOptimistic(
    state.tickets,
    (current: ProjectTicket[], update: { id: string; priority: ProjectTicket['priority'] }) =>
      current.map(ticket =>
        ticket.id === update.id ? { ...ticket, priority: update.priority } : ticket
      )
  );

  // 3. Combined handler coordinating transition and optimistic projection
  function handlePriorityChange(ticketId: string, priority: ProjectTicket['priority']) {
    startTransition(async () => {
      // Optimistically project next state immediately
      setOptimisticTickets({ id: ticketId, priority });

      // Build payload and trigger the Action State reducer
      const formData = new FormData();
      formData.set('ticketId', ticketId);
      formData.set('priority', priority);

      await formAction(formData);
    });
  }

  const isBusy = isActionPending || isTransitionPending;

  return (
    <div className="kanban-group">
      <header>
        <h3>Active Tasks</h3>
        {isBusy && <span className="sync-indicator">Synchronizing changes...</span>}
      </header>

      {state.error && (
        <div role="alert" className="error-callout">
          <strong>Rollback triggered:</strong> {state.error}
        </div>
      )}

      <ul className="task-list">
        {optimisticTickets.map(ticket => (
          <li key={ticket.id} className={`ticket-card priority-${ticket.priority.toLowerCase()}`}>
            <span>{ticket.title}</span>
            <div className="btn-group">
              {(['LOW', 'MEDIUM', 'HIGH'] as const).map(p => (
                <button
                  key={p}
                  disabled={ticket.priority === p || isBusy}
                  onClick={() => handlePriorityChange(ticket.id, p)}
                >
                  {p}
                </button>
              ))}
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
}

5. use(Resource): reading promises and contexts during render

use unwraps a resource directly during render, and unlike other hooks, it can be called conditionally — inside an if block or a loop. Passed a promise, it integrates directly with Suspense: a pending promise suspends the component, a resolved one returns synchronously, and a rejection propagates to the surrounding error boundary. Promises read with use must be cached outside render, or you get an infinite suspension loop.

import React, { use, Suspense, createContext } from 'react';

interface AuthSession {
  user: { name: string; permissions: string[] };
}

const PermissionContext = createContext<{ requiredLevel: string } | null>(null);

function UserDashboardPanel({
  sessionPromise,
  checkElevatedAccess
}: {
  sessionPromise: Promise<AuthSession>;
  checkElevatedAccess: boolean;
}) {
  // 1. Unwrapping a promise during render
  const session = use(sessionPromise);

  // 2. Calling use() conditionally inside control flow
  let accessLevel = 'Standard';
  if (checkElevatedAccess) {
    const permConfig = use(PermissionContext);
    accessLevel = permConfig ? permConfig.requiredLevel : 'Elevated';
  }

  return (
    <div className="dashboard-content">
      <h4>Welcome, {session.user.name}</h4>
      <p>Authorization Level: {accessLevel}</p>
      <ul>
        {session.user.permissions.map(p => <li key={p}>{p}</li>)}
      </ul>
    </div>
  );
}

export function AppDashboard({ sessionPromise }: { sessionPromise: Promise<AuthSession> }) {
  return (
    <PermissionContext value={{ requiredLevel: 'Admin-Operations' }}>
      <Suspense fallback={<div className="skeleton-loader">Authenticating...</div>}>
        <UserDashboardPanel
          sessionPromise={sessionPromise}
          checkElevatedAccess={true}
        />
      </Suspense>
    </PermissionContext>
  );
}

Why does creating an unmemoized promise inside a component body and passing it to use() trigger an infinite loop? Instantiating a promise inline means every render creates a new reference. use() suspends on the pending one, React mounts the fallback, the promise resolves, React re-renders — and re-creates yet another fresh, unresolved promise. use() suspends on that one, forever. Promises read with use() must be cached: via a loader, a cache client, or a module-level cache.

6. useFormStatus: hierarchical form status

useFormStatus reads the submission status of the nearest ancestor <form> — { pending, data, method, action } — without prop-drilling it down to children.

import React, { useActionState } from 'react';
import { useFormStatus } from 'react-dom';

function ComplexFormSubmissionControls() {
  const { pending, data, method } = useFormStatus();

  return (
    <footer className="form-footer">
      {pending && data && (
        <span className="upload-indicator" aria-live="polite">
          Saving changes for: {data.get('username')?.toString()} via {method}...
        </span>
      )}
      <button
        type="submit"
        disabled={pending}
        aria-busy={pending}
        className="primary-btn"
      >
        {pending ? 'Committing Record...' : 'Confirm Submission'}
      </button>
    </footer>
  );
}

export function AccountSettingsForm() {
  const [, dispatch] = useActionState(async (_: unknown, formData: FormData) => {
    await fetch('/api/account', { method: 'POST', body: formData });
    return null;
  }, null);

  return (
    <form action={dispatch} className="settings-panel">
      <label htmlFor="username-field">Account Handle:</label>
      <input id="username-field" name="username" type="text" defaultValue="octocat" />

      {/* useFormStatus executes inside the child tree */}
      <ComplexFormSubmissionControls />
    </form>
  );
}

Why does useFormStatus return pending === false when called directly in the component that renders the parent <form>? The host <form> injects a context provider around its children — and React context can never be read by the component rendering the provider itself, only by descendants. Call the hook from a child placed inside the <form> tags.

7. useEffectEvent: separating reactive triggers from event logic

useEffectEvent fixes the mixed-reactive-dependency problem in effects. Historically, if an effect needed a dynamic prop or piece of state, that value had to join the dependency array — re-running the effect every time it changed, even when the change wasn't meant to restart anything. useEffectEvent extracts the non-reactive logic into a stable callback that reads the latest props and state without becoming a dependency.

import React, { useEffect, useEffectEvent } from 'react';

interface NotificationSocketProps {
  roomChannelId: string;
  theme: 'light' | 'dark';
  onLogAnalytics: (metric: string, meta: Record<string, unknown>) => void;
}

export function NotificationSocket({
  roomChannelId,
  theme,
  onLogAnalytics
}: NotificationSocketProps) {
  // Extract the non-reactive callback
  const onIncomingNotification = useEffectEvent((payload: { message: string }) => {
    // Reads the latest theme and analytics handler without making them reactive triggers
    onLogAnalytics('socket_message_received', {
      channel: roomChannelId,
      appliedTheme: theme,
      length: payload.message.length,
    });
  });

  useEffect(() => {
    const socket = new WebSocket(`wss://stream.internal/rooms/${roomChannelId}`);

    socket.onmessage = (event) => {
      const parsed = JSON.parse(event.data);
      onIncomingNotification(parsed);
    };

    return () => {
      socket.close();
    };
    // theme and onLogAnalytics are safely excluded from dependencies
  }, [roomChannelId]);

  return <aside className={`socket-status-badge theme-${theme}`}>Connected: {roomChannelId}</aside>;
}

What's the fundamental difference between useCallback and useEffectEvent? useCallback is reactive — it exists to preserve referential equality, has its own dependency array, and returns a new function reference whenever those dependencies change. useEffectEvent is explicitly non-reactive: no dependency array, it can't be used as a JSX event-handler prop, and it can only be called from inside effects. It lets an effect read current state and props without treating those values as synchronization triggers.

8. Modern useRef patterns and cleanup functions

ref is now a standard prop on function components, which deprecates the need for forwardRef wrappers. Refs still serve their two jobs — pointing at persistent DOM nodes and holding mutable instance variables across renders without triggering a re-render — but callback refs now support cleanup functions, matching effect-cleanup behavior.

import React, { useRef, useState } from 'react';

interface CustomTextInputProps {
  label: string;
  ref?: React.Ref<HTMLInputElement>;
}

// React 19: ref is accepted directly as a prop; no forwardRef needed
export function CustomTextInput({ label, ref }: CustomTextInputProps) {
  return (
    <div className="input-field">
      <label>{label}</label>
      <input ref={ref} type="text" className="styled-input" />
    </div>
  );
}

export function AutoFocusManager() {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [active, setActive] = useState(false);

  return (
    <div>
      <button onClick={() => setActive(v => !v)}>Toggle Input</button>
      {active && (
        <CustomTextInput
          label="Profile Display Name"
          // Callback ref with a cleanup function (React 19)
          ref={(node) => {
            if (node) {
              node.focus();
              console.log('DOM node attached and focused');
            }
            return () => {
              console.log('DOM node unmounted; cleaning up listeners/timers');
            };
          }}
        />
      )}
    </div>
  );
}

Why does mutating ref.current during render break concurrent rendering and the React Compiler? Rendering must be a pure calculation with no observable side effects — concurrent rendering may evaluate a component multiple times, pause a render pass to handle an urgent update, or abandon one entirely. Mutating ref.current during render makes the output non-deterministic, can cause tearing, and breaks the Compiler's static data-flow model. Refs should only be read or mutated inside effects or event handlers.

9. Native form operations, FormData, and action resets

React 19 leans on the platform: <form action={fn}>, <button formAction={fn}>, and requestFormReset(). Submitting a form triggers a concurrent transition, passes a populated FormData to the action, and resets uncontrolled fields when the action succeeds.

import React, { useActionState } from 'react';
import { requestFormReset } from 'react-dom';

export function DocumentEditor() {
  const [status, formAction, isPending] = useActionState(
    async (_: string | null, formData: FormData) => {
      const intent = formData.get('intent')?.toString();
      const documentTitle = formData.get('title')?.toString();
      const content = formData.get('content')?.toString();

      if (intent === 'draft') {
        await fetch('/api/drafts', {
          method: 'POST',
          body: JSON.stringify({ documentTitle, content }),
        });
        return 'Draft saved successfully.';
      } else {
        await fetch('/api/publish', {
          method: 'POST',
          body: JSON.stringify({ documentTitle, content }),
        });
        return 'Document published globally.';
      }
    },
    null
  );

  return (
    <form action={formAction} className="editor-form">
      {status && <div role="status">{status}</div>}

      <label htmlFor="doc-title">Document Title</label>
      <input id="doc-title" name="title" required defaultValue="" />

      <label htmlFor="doc-content">Body</label>
      <textarea id="doc-content" name="content" required defaultValue="" />

      <div className="button-row">
        {/* Multi-action submit targets using formAction */}
        <button
          type="submit"
          name="intent"
          value="draft"
          disabled={isPending}
        >
          Save Draft
        </button>

        <button
          type="submit"
          name="intent"
          value="publish"
          disabled={isPending}
          className="btn-accent"
        >
          Publish
        </button>

        <button
          type="button"
          onClick={(e) => requestFormReset(e.currentTarget.form!)}
          disabled={isPending}
        >
          Reset Fields
        </button>
      </div>
    </form>
  );
}

10. Schema validation with Zod v4 in modern Actions

Validating untrusted form input still needs a runtime schema, and Zod v4 changes the shape of that code: top-level validator functions like z.email(), z.uuid(), and z.url() replace chained methods such as z.string().email() for better tree-shaking; a unified error parameter replaces the old invalid_type_error/required_error options; top-level formatters like z.flattenError(result.error) give you structured field errors; and coercive schemas such as z.coerce.number() now type their input as unknown to keep parsing honest.

import React, { useActionState } from 'react';
import * as z from 'zod';

// Zod v4: string formats are top-level, tree-shakeable functions
const UserRegistrationSchema = z.object({
  username: z.string({
    error: (issue) => issue.input === undefined ? 'Username is required' : 'Invalid username'
  }).min(3, { error: 'Username must be at least 3 characters long' }),

  // Top-level validator function in Zod 4
  email: z.email({ error: 'A valid email address is required' }),

  // Zod 4: z.coerce input type is unknown
  age: z.coerce.number({ error: 'Age must be a valid number' }).min(18, { error: 'Must be at least 18' }),
});

type FormFieldValues = {
  username: string;
  email: string;
  age: string;
};

type ActionValidationState = {
  status: 'idle' | 'success' | 'validation_error' | 'server_error';
  fieldErrors: Partial<Record<keyof FormFieldValues, string[]>>;
  formErrors: string[];
  values: FormFieldValues;
};

export function RegistrationForm() {
  const [state, formAction, isPending] = useActionState<ActionValidationState, FormData>(
    async (prevState, formData) => {
      const rawValues: FormFieldValues = {
        username: formData.get('username')?.toString() ?? '',
        email: formData.get('email')?.toString() ?? '',
        age: formData.get('age')?.toString() ?? '',
      };

      // Run runtime parsing
      const validationResult = UserRegistrationSchema.safeParse(rawValues);

      if (!validationResult.success) {
        // Zod v4: standalone, top-level error formatter
        const flattened = z.flattenError(validationResult.error);
        return {
          status: 'validation_error',
          fieldErrors: flattened.fieldErrors,
          formErrors: flattened.formErrors,
          values: rawValues,
        };
      }

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

        if (!res.ok) {
          return {
            status: 'server_error',
            fieldErrors: {},
            formErrors: ['Registration failed on server.'],
            values: rawValues,
          };
        }

        return {
          status: 'success',
          fieldErrors: {},
          formErrors: [],
          values: { username: '', email: '', age: '' },
        };
      } catch (err) {
        return {
          status: 'server_error',
          fieldErrors: {},
          formErrors: [err instanceof Error ? err.message : 'Unknown network failure'],
          values: rawValues,
        };
      }
    },
    {
      status: 'idle',
      fieldErrors: {},
      formErrors: [],
      values: { username: '', email: '', age: '' },
    }
  );

  return (
    <form action={formAction} className="registration-box">
      {state.formErrors.length > 0 && (
        <div role="alert" className="error-summary">
          {state.formErrors.map((err, i) => <p key={i}>{err}</p>)}
        </div>
      )}

      <div>
        <label htmlFor="reg-user">Username</label>
        <input
          id="reg-user"
          name="username"
          defaultValue={state.values.username}
          disabled={isPending}
        />
        {state.fieldErrors.username && (
          <span className="field-err">{state.fieldErrors.username[0]}</span>
        )}
      </div>

      <div>
        <label htmlFor="reg-email">Email</label>
        <input
          id="reg-email"
          name="email"
          type="email"
          defaultValue={state.values.email}
          disabled={isPending}
        />
        {state.fieldErrors.email && (
          <span className="field-err">{state.fieldErrors.email[0]}</span>
        )}
      </div>

      <div>
        <label htmlFor="reg-age">Age</label>
        <input
          id="reg-age"
          name="age"
          type="number"
          defaultValue={state.values.age}
          disabled={isPending}
        />
        {state.fieldErrors.age && (
          <span className="field-err">{state.fieldErrors.age[0]}</span>
        )}
      </div>

      <button type="submit" disabled={isPending}>
        {isPending ? 'Validating...' : 'Submit Form'}
      </button>
    </form>
  );
}

11. Deterministic server-rendering bailouts via browser()

Components that depend on browser-only APIs — localStorage, window.matchMedia, a canvas context — used to need a client-only check flag inside useEffect, forcing a slow secondary render pass on the client. React 19.3's browser(), from react-dom, fixes that: called as use(browser(reason)), it suspends rendering during SSR and tells the server streaming renderer to leave the nearest <Suspense> boundary in its fallback state. In the browser runtime, use(browser()) simply resolves to undefined and the component renders normally.

import React, { Suspense, use, useState } from 'react';
import { browser } from 'react-dom';

function LocalStoragePersistedNotes() {
  // Opt out of server-side rendering deterministically
  use(browser('Component requires browser-only localStorage access.'));

  // Executes safely on the client only; no hydration mismatch
  const [note, setNote] = useState(() => {
    return localStorage.getItem('draft-scratchpad') ?? '';
  });

  function handleSave(e: React.ChangeEvent<HTMLTextAreaElement>) {
    const val = e.target.value;
    setNote(val);
    localStorage.setItem('draft-scratchpad', val);
  }

  return (
    <div className="scratchpad">
      <h4>Local Scratchpad</h4>
      <textarea value={note} onChange={handleSave} rows={4} />
    </div>
  );
}

export function NotesApp() {
  return (
    <div className="notes-container">
      <h2>Collaborative Workspace</h2>
      <Suspense fallback={<div className="skeleton-box">Initializing client workspace...</div>}>
        <LocalStoragePersistedNotes />
      </Suspense>
    </div>
  );
}

Why is use(browser()) architecturally better than the classic useEffect(() => setMounted(true), []) workaround? The mounted-flag pattern forces an extra render cycle: the component first mounts with empty or mismatched markup, then re-renders once the effect flips mounted to true — a layout shift and extra Total Blocking Time. use(browser()) integrates directly with Suspense during SSR: the server streams the fallback HTML immediately and skips the browser-only subtree entirely. On the client, hydration renders the component straight into its resolved state, with no mismatched intermediate pass.

12. Declarative animation with <ViewTransition> and addTransitionType

React 19.3 stabilizes <ViewTransition>, letting DOM elements animate with the browser's View Transition API during concurrent transitions. addTransitionType annotates a transition with a custom type that maps to the CSS :active-view-transition-type(...) pseudo-selector, so different interactions can drive different animations.

import React, { useState, useTransition, addTransitionType, ViewTransition } from 'react';

interface FeedItem {
  id: string;
  title: string;
  category: string;
}

export function AnimatedFeedManager({ items }: { items: FeedItem[] }) {
  const [filter, setFilter] = useState('ALL');
  const [isPending, startTransition] = useTransition();

  function handleFilterSelection(category: string) {
    startTransition(() => {
      // Annotate the transition cause for CSS pseudo-selector scoping
      addTransitionType('category-filter-change');
      setFilter(category);
    });
  }

  const visibleItems = filter === 'ALL'
    ? items
    : items.filter(item => item.category === filter);

  return (
    <div className="feed-container">
      <nav className="filter-tabs">
        {['ALL', 'TECH', 'DESIGN'].map(cat => (
          <button
            key={cat}
            disabled={filter === cat || isPending}
            onClick={() => handleFilterSelection(cat)}
          >
            {cat}
          </button>
        ))}
      </nav>

      {/* Declarative view transition wrapper */}
      <ViewTransition default="auto">
        <div className="card-grid">
          {visibleItems.map(item => (
            <ViewTransition
              key={item.id}
              name={`card-${item.id}`}
              enter="fade-slide-in"
              exit="fade-slide-out"
            >
              <article className="feed-card">
                <h5>{item.title}</h5>
                <span className="tag">{item.category}</span>
              </article>
            </ViewTransition>
          ))}
        </div>
      </ViewTransition>
    </div>
  );
}

The accompanying CSS scopes animations to the transition type React assigned:

:root:active-view-transition-type(category-filter-change) {
  ::view-transition-group(*) {
    animation-duration: 300ms;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }
}

::view-transition-old(.fade-slide-out) {
  opacity: 0;
  transform: translateY(10px);
}

::view-transition-new(.fade-slide-in) {
  opacity: 1;
  transform: translateY(0);
}

13. Multi-node DOM interactivity with Fragment refs

React 19.3 allows refs on plain <Fragment> elements. Instead of returning a single DOM node, the ref resolves to a FragmentInstance that supports event handling, layout measurement, and intersection observation across every top-level child — with no wrapper <div> needed.

import React, { Fragment, useRef, useLayoutEffect } from 'react';

interface DefinitionItem {
  id: string;
  term: string;
  description: string;
}

export function GlossaryTracker({ terms }: { terms: DefinitionItem[] }) {
  // Fragment ref typed to hold a FragmentInstance
  const fragmentRef = useRef<any>(null);

  useLayoutEffect(() => {
    const fragmentInstance = fragmentRef.current;
    if (!fragmentInstance) return;

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.classList.add('in-viewport');
          }
        });
      },
      { threshold: 0.5 }
    );

    // Observe all top-level DOM nodes in the Fragment without a parent wrapper
    fragmentInstance.observeUsing(observer);

    return () => {
      fragmentInstance.unobserveUsing(observer);
      observer.disconnect();
    };
  }, []);

  return (
    <dl className="semantic-glossary">
      {/* Fragment ref enables direct multi-node observation */}
      <Fragment ref={fragmentRef}>
        {terms.map((item) => (
          <Fragment key={item.id}>
            <dt className="term-entry">{item.term}</dt>
            <dd className="definition-entry">{item.description}</dd>
          </Fragment>
        ))}
      </Fragment>
    </dl>
  );
}

The React Compiler: architecture and the memoization shift

The React Compiler moves performance optimization from manual memoization to automated, build-time static analysis. Developers used to spend real effort managing useMemo/useCallback dependency arrays and wrapping components in React.memo to cut unnecessary re-renders; the compiler does that work at build time instead.

It runs as a Babel plugin (babel-plugin-react-compiler) that converts the JS/TS AST into a High-Level Intermediate Representation, structures it as a Control Flow Graph, and converts that to Static Single Assignment form. Through escape analysis, alias tracking, and type inference, it determines the precise mutability boundaries and reactive dependencies of every value in a component or hook — then injects fine-grained memoization slots directly into the output, caching individual sub-expressions, JSX allocations, and derived computations so each one only re-evaluates when its own inputs change.

Dimension Manual memoization React Compiler
Execution point Runtime dependency-array comparison during reconciliation Build-time IR analysis with selective caching injection
Granularity Coarse — limited to explicitly wrapped values or components Sub-expression level — individual JSX elements and calculations cached independently
Developer overhead High — manual dependency arrays, prone to stale closures Low — plain idiomatic JS that follows the Rules of React
Failure modes Missing deps cause stale state; excess deps cause over-rendering Rule-violating code bails out of optimization, or throws at runtime
Cache lifetime Hook-bound, re-evaluated on shallow reference inequality Optimized slot caching, skipped when the surrounding scope is unmutated

Compilation modes and directives

The compiler offers several strategies for incremental adoption. Infer mode, the default, analyzes naming conventions and automatically optimizes anything that looks like a component (PascalCase) or a hook (use*). Annotation mode restricts compilation to functions explicitly marked with a "use memo" directive — useful for a phased rollout on a large legacy codebase. All mode forces compilation on every top-level function and bails out only when the syntax rules are violated.

When a component talks to a non-compliant third-party library, mutates during render, or otherwise breaks the Rules of React in a way you can't immediately refactor, drop "use no memo" at the top of the function body to opt it out:

function LegacyDataGrid({ records }: { records: DataRecord[] }) {
  "use no memo"; // Bails out of compiler optimization; runs as standard JS
  // Component implementation with intentional local mutations
  return <table>{/* ... */}</table>;
}

The pitfall: memoization for correctness

A critical distinction under the compiler is memoization for performance versus memoization for correctness. Older codebases often lean on useMemo/useCallback to force referential stability so a downstream effect dependency or a shallow-equality check won't fire:

System Correctness ≠ f(Performance Caching)

React has always said memoization is a performance optimization, not a semantic guarantee — the runtime can evict a cache under memory pressure. Because the compiler derives its memoization boundaries from data flow rather than developer annotation, code that depends on referential instability to trigger an effect, or on manual memoization to suppress an infinite effect loop, can break once the compiler is switched on.

To stay stable under the compiler: keep render logic strictly pure and idempotent, confine side effects to event handlers or external-system synchronization, and never mutate props, state, or values created outside the component's own render scope.

If the Compiler handles memoization automatically, should we delete every useMemo and useCallback in the codebase? Not blindly. The compiler preserves existing manual memoization and won't break the build with it in place — remove it incrementally once compiler coverage is verified. The one case that needs real work first is memoization used for correctness, like keeping an object reference stable so a downstream effect's dependency check won't fire. There, fix the underlying anti-pattern — useEffectEvent, or synchronizing state during render — before you touch the memoization.

A staff-level review checklist

Staff-level review prioritizes architectural integrity, runtime predictability, and scalability over cosmetic syntax preferences. When reviewing a junior engineer's diff, focus on the mechanics of reactivity, state derivation, and concurrency boundaries first.

Concurrency and Actions. Independent boolean flags (isLoading, hasError, isSuccess) create conflicting intermediate states and need manual cleanup to avoid leaks. Check whether async mutations are modeled with useActionState, or wrapped in an async useTransition, to get native pending tracking, error isolation, and sequential dispatch for free. Form handlers built on onSubmit + e.preventDefault() should move to native <form action={fn}>, which supports uncontrolled inputs, automatic resets, and progressive enhancement. Optimistic updates should go through useOptimistic, not a manual cache snapshot with an imperative rollback.

State topology. Storing derived state in useState and syncing it via useEffect is the single most common anti-pattern — it adds a render pass, causes layout thrashing, and risks infinite loops. Anything computable from existing props or state should be computed synchronously during render. State should live with the component that consumes it, not be hoisted to a common ancestor "just in case." And manual memoization applied to trivial calculations should be flagged for removal — the compiler already handles it.

Effect boundaries. Effects exist to synchronize a component with an external system — a socket, a DOM observer, a non-React widget — not to shuttle data between components or respond to user events. Any unavoidable async call inside an effect needs an AbortController to prevent out-of-order resolution. Non-reactive logic inside an effect — telemetry, logging, reading a transient preference — belongs in useEffectEvent, not the dependency array.

Resources and Suspense. Fetching inside useEffect on mount produces network waterfalls, layout shift, and boilerplate loading state. Prefer unwrapping a cached resource with use(Promise) behind a granular <Suspense> boundary, so a slow secondary dependency doesn't block the primary UI shell.

Types, contracts, accessibility. Loose optional bags ({ data?: T; error?: string; loading?: boolean }) should become discriminated unions that make invalid states unrepresentable. Form payloads should go through a schema (Zod) rather than an unsafe assertion on formData.get(). And every interactive element needs semantic HTML, labels associated by htmlFor/id, aria-busy alongside disabled on anything async, and role="alert"/aria-live on error banners.

Code review scenario: user profile management

The junior version below is a composite of the anti-patterns above: multi-flag state, derived state synced through an effect, unprotected fetching, and imperative form handling.

// Junior implementation: UserProfileCard.tsx
import React, { useState, useEffect, useMemo, useCallback } from 'react';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
}

export const UserProfileCard = ({ userId }: { userId: string }) => {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Local form inputs
  const [name, setName] = useState('');
  const [role, setRole] = useState('');

  // Derived state stored in useState and updated via useEffect
  const [isValid, setIsValid] = useState(false);

  // Unnecessary manual memoization
  const fetchHeaders = useMemo(() => ({
    'Authorization': 'Bearer token-xyz',
    'Content-Type': 'application/json'
  }), []);

  // Fetching data inside useEffect without abort handling
  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`, { headers: fetchHeaders })
      .then(res => res.json())
      .then((data: User) => {
        setUser(data);
        setName(data.name);
        setRole(data.role);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [userId, fetchHeaders]);

  // Derived-state synchronization anti-pattern
  useEffect(() => {
    if (name.trim().length > 2 && role.trim().length > 0) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
  }, [name, role]);

  // Imperative form submission with race conditions and manual loading state
  const handleSubmit = useCallback(async (e: React.FormEvent) => {
    e.preventDefault();
    if (!isValid) return;

    setLoading(true);
    try {
      const response = await fetch(`/api/users/${userId}`, {
        method: 'PUT',
        headers: fetchHeaders,
        body: JSON.stringify({ name, role })
      });
      const updated = await response.json();
      setUser(updated);
      alert('Profile updated successfully!');
    } catch (err: any) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }, [userId, name, role, isValid, fetchHeaders]);

  if (loading && !user) return <div>Loading user profile...</div>;
  if (error) return <div style={{ color: 'red' }}>Error: {error}</div>;
  if (!user) return null;

  return (
    <div className="card">
      <h2>Edit User: {user.name}</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label>Name:</label>
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </div>
        <div>
          <label>Role:</label>
          <input
            type="text"
            value={role}
            onChange={(e) => setRole(e.target.value)}
          />
        </div>
        <button type="submit" disabled={!isValid || loading}>
          {loading ? 'Saving...' : 'Save Changes'}
        </button>
      </form>
    </div>
  );
};

The critique: isValid is purely derived from name and role, so storing it in state and recomputing it in an effect adds an unnecessary render on every keystroke — it should be a synchronous calculation during render. The fetch inside useEffect has no AbortController, so if userId changes quickly, an older response can resolve after a newer one and overwrite it with stale data. The loading/error/user triple permits inconsistent states like loading === true next to an active error message — better modeled as a discriminated union under useActionState, which tracks pending execution natively. Controlling every input with useState re-renders the whole component on each keystroke, where defaultValue plus FormData on submit would avoid the churn entirely. There's no optimistic feedback — saving blocks the UI instead of updating visible details immediately and reverting on failure. The useMemo/useCallback pairs add boilerplate the Compiler already makes redundant. And accessibility is missing throughout: no htmlFor/id pairing, no role="alert" on the error, no aria-busy on the submit button.

The refactor below unwraps the resource via use(Promise) inside Suspense, submits through useActionState, projects optimistic updates through useOptimistic, delegates the submit button's state to useFormStatus, and types the transitions as a discriminated union.

// Staff implementation: UserProfileCard.tsx
import React, { use, useActionState, useOptimistic, useId } from 'react';
import { useFormStatus } from 'react-dom';

export interface User {
  id: string;
  name: string;
  email: string;
  role: string;
}

type ActionState =
  | { status: 'idle'; data: User; error: null }
  | { status: 'success'; data: User; error: null }
  | { status: 'error'; data: User; error: string };

interface UserProfileCardProps {
  userPromise: Promise<User>;
  onUpdateUser: (userId: string, formData: FormData) => Promise<User>;
}

// Subordinate component reading the parent form's submission state
function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button
      type="submit"
      disabled={pending}
      aria-busy={pending}
      className="btn-primary"
    >
      {pending ? 'Saving Changes...' : 'Save Changes'}
    </button>
  );
}

export function UserProfileCard({ userPromise, onUpdateUser }: UserProfileCardProps) {
  // Read the resolved promise directly from the Suspense cache
  const initialUser = use(userPromise);
  const nameInputId = useId();
  const roleInputId = useId();

  // Unified Action state handling the async lifecycle
  const [state, formAction] = useActionState<ActionState, FormData>(
    async (previousState, formData) => {
      try {
        const updatedUser = await onUpdateUser(initialUser.id, formData);
        return { status: 'success', data: updatedUser, error: null };
      } catch (err) {
        return {
          status: 'error',
          data: previousState.data,
          error: err instanceof Error ? err.message : 'An unknown error occurred'
        };
      }
    },
    { status: 'idle', data: initialUser, error: null }
  );

  // Optimistic UI projection
  const [optimisticUser, setOptimisticUser] = useOptimistic(
    state.data,
    (current: User, optimisticUpdate: { name: string; role: string }) => ({
      ...current,
      name: optimisticUpdate.name,
      role: optimisticUpdate.role,
    })
  );

  // Action wrapper handling the optimistic trigger alongside the form dispatch
  async function handleAction(formData: FormData) {
    const rawName = formData.get('name')?.toString() ?? '';
    const rawRole = formData.get('role')?.toString() ?? '';

    // Optimistically update before the server responds
    setOptimisticUser({ name: rawName, role: rawRole });
    await formAction(formData);
  }

  return (
    <section className="profile-card" aria-labelledby="profile-heading">
      <header>
        <h2 id="profile-heading">Edit Profile: {optimisticUser.name}</h2>
        <span className="badge">{optimisticUser.role}</span>
      </header>

      {state.status === 'error' && (
        <div role="alert" className="error-banner" aria-live="assertive">
          {state.error}
        </div>
      )}

      {/* React 19 native action handler using uncontrolled inputs */}
      <form action={handleAction} className="profile-form">
        <div className="form-group">
          <label htmlFor={nameInputId}>Full Name</label>
          <input
            id={nameInputId}
            name="name"
            type="text"
            required
            minLength={3}
            defaultValue={state.data.name}
            aria-describedby={state.status === 'error' ? 'form-error' : undefined}
          />
        </div>

        <div className="form-group">
          <label htmlFor={roleInputId}>Role</label>
          <input
            id={roleInputId}
            name="role"
            type="text"
            required
            defaultValue={state.data.role}
          />
        </div>

        <footer className="form-actions">
          <SubmitButton />
        </footer>
      </form>
    </section>
  );
}

Practical exercises

Exercise 1: async orchestration and optimistic mutations

Build a task-assignment widget where picking a new collaborator reflects immediately in the UI, and a failed server update automatically reverts to the previous assignee with an accessible error — with no manual useState flags.

import React, { useActionState, useOptimistic, useId } from 'react';

interface Assignee {
  id: string;
  name: string;
  avatarUrl: string;
}

interface TaskState {
  taskId: string;
  assignee: Assignee;
  error: string | null;
}

interface TaskWidgetProps {
  initialTask: { id: string; assignee: Assignee };
  availableAssignees: Assignee[];
  onAssignTask: (taskId: string, assigneeId: string) => Promise<Assignee>;
}

export function TaskAssignmentWidget({
  initialTask,
  availableAssignees,
  onAssignTask
}: TaskWidgetProps) {
  const selectId = useId();

  const [state, formAction, isPending] = useActionState<TaskState, FormData>(
    async (prevState, formData) => {
      const selectedId = formData.get('assigneeId') as string;
      const targetAssignee = availableAssignees.find(a => a.id === selectedId);

      if (!targetAssignee) {
        return { ...prevState, error: 'Invalid team member selected' };
      }

      try {
        const persistedAssignee = await onAssignTask(prevState.taskId, selectedId);
        return { taskId: prevState.taskId, assignee: persistedAssignee, error: null };
      } catch (err) {
        return {
          ...prevState,
          error: err instanceof Error ? err.message : 'Failed to update assignment'
        };
      }
    },
    { taskId: initialTask.id, assignee: initialTask.assignee, error: null }
  );

  const [optimisticAssignee, setOptimisticAssignee] = useOptimistic(
    state.assignee,
    (_current, nextAssignee: Assignee) => nextAssignee
  );

  async function clientAction(formData: FormData) {
    const selectedId = formData.get('assigneeId') as string;
    const target = availableAssignees.find(a => a.id === selectedId);
    if (target) {
      setOptimisticAssignee(target);
    }
    await formAction(formData);
  }

  return (
    <div className="task-assignment-widget">
      <div className="current-assignee">
        <img
          src={optimisticAssignee.avatarUrl}
          alt={`${optimisticAssignee.name}'s profile avatar`}
          className="avatar"
        />
        <span>Assigned to: <strong>{optimisticAssignee.name}</strong></span>
      </div>

      {state.error && (
        <p role="alert" className="error-message" aria-live="polite">
          {state.error}
        </p>
      )}

      <form action={clientAction}>
        <label htmlFor={selectId}>Reassign Team Member:</label>
        <select
          id={selectId}
          name="assigneeId"
          defaultValue={state.assignee.id}
          disabled={isPending}
        >
          {availableAssignees.map((person) => (
            <option key={person.id} value={person.id}>
              {person.name}
            </option>
          ))}
        </select>
        <button type="submit" disabled={isPending} aria-busy={isPending}>
          {isPending ? 'Reassigning...' : 'Confirm'}
        </button>
      </form>
    </div>
  );
}

Exercise 2: reactive isolation with useEffectEvent and Suspense

Read an async resource via use(Promise) and isolate non-reactive telemetry with useEffectEvent. Incoming chat messages get logged with the user's active theme and opt-out preference — without those preferences ever re-triggering the WebSocket subscription effect.

import React, { use, useEffect, useEffectEvent } from 'react';

interface ChatMessage {
  id: string;
  sender: string;
  text: string;
}

interface ChannelProps {
  channelId: string;
  theme: 'dark' | 'light';
  analyticsOptOut: boolean;
  messagesPromise: Promise<ChatMessage[]>;
  subscribeToChannel: (channelId: string, onMessage: (msg: ChatMessage) => void) => () => void;
  sendTelemetry: (eventName: string, meta: Record<string, unknown>) => void;
}

export function LiveChatChannel({
  channelId,
  theme,
  analyticsOptOut,
  messagesPromise,
  subscribeToChannel,
  sendTelemetry
}: ChannelProps) {
  // Read the initial message payload during render via the Suspense contract
  const initialMessages = use(messagesPromise);

  // Extract the non-reactive analytics logic out of the reactive effect scope
  const onMessageReceived = useEffectEvent((message: ChatMessage) => {
    if (!analyticsOptOut) {
      sendTelemetry('message_displayed', {
        channelId,
        messageId: message.id,
        appliedTheme: theme,
        timestamp: Date.now()
      });
    }
  });

  useEffect(() => {
    // The effect isolates its reactive dependency strictly to channelId
    const unsubscribe = subscribeToChannel(channelId, (newMessage) => {
      // Calls the Effect Event, reading the latest theme without re-triggering this effect
      onMessageReceived(newMessage);
    });

    return () => {
      unsubscribe();
    };
  }, [channelId]); // Explicitly clean dependency list: no theme or analyticsOptOut

  return (
    <section className={`chat-channel theme-${theme}`}>
      <h3>Channel: {channelId}</h3>
      <ul className="message-list">
        {initialMessages.map(msg => (
          <li key={msg.id}>
            <strong>{msg.sender}:</strong> {msg.text}
          </li>
        ))}
      </ul>
    </section>
  );
}

Exercise 3: declarative transitions with <ViewTransition>

Build a product gallery where <ViewTransition> and addTransitionType animate sorting and selection separately: sorting triggers a smooth re-order, and selecting a product triggers an expansion scoped through its own CSS pseudo-selector.

import React, { useState, useTransition, addTransitionType, ViewTransition } from 'react';

interface Product {
  id: string;
  title: string;
  price: number;
  imageUrl: string;
}

export function ProductGallery({ products }: { products: Product[] }) {
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');
  const [isPending, startTransition] = useTransition();

  const sortedProducts = [...products].sort((a, b) =>
    sortOrder === 'asc' ? a.price - b.price : b.price - a.price
  );

  function handleSortChange(order: 'asc' | 'desc') {
    startTransition(() => {
      // Annotate the transition cause for CSS selector scoping
      addTransitionType('grid-resort');
      setSortOrder(order);
    });
  }

  function handleSelectProduct(id: string | null) {
    startTransition(() => {
      addTransitionType(id ? 'item-expand' : 'item-collapse');
      setSelectedId(id);
    });
  }

  return (
    <div className="product-gallery">
      <div className="controls">
        <button
          onClick={() => handleSortChange(sortOrder === 'asc' ? 'desc' : 'asc')}
          disabled={isPending}
        >
          Sort: {sortOrder.toUpperCase()}
        </button>
      </div>

      <ViewTransition default="auto">
        <div className="product-grid">
          {sortedProducts.map((product) => (
            <ViewTransition
              key={product.id}
              name={`product-card-${product.id}`}
              enter="fade-in"
              exit="fade-out"
            >
              <article
                className={`card ${selectedId === product.id ? 'active-detail' : ''}`}
                onClick={() => handleSelectProduct(
                  selectedId === product.id ? null : product.id
                )}
              >
                <img src={product.imageUrl} alt={product.title} />
                <h4>{product.title}</h4>
                <p>${product.price.toFixed(2)}</p>
              </article>
            </ViewTransition>
          ))}
        </div>
      </ViewTransition>
    </div>
  );
}

The matching CSS targets the active view-transition types React assigns:

:root:active-view-transition-type(grid-resort) {
  ::view-transition-group(*) {
    animation-duration: 250ms;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  }
}

:root:active-view-transition-type(item-expand) {
  ::view-transition-old(*) {
    animation-duration: 350ms;
    transform-origin: center;
  }
  ::view-transition-new(*) {
    animation-duration: 350ms;
  }
}

Exercise 4: Fragment refs without layout wrappers

Track visibility across sibling elements with React 19.3 Fragment refs, without wrapping them in extra <div> containers and while keeping the semantic <dl> structure intact.

import React, { Fragment, useRef, useLayoutEffect } from 'react';

interface ContentItem {
  id: string;
  title: string;
  description: string;
}

interface SectionListTrackerProps {
  items: ContentItem[];
  onItemsVisible: (visibleIds: string[]) => void;
}

export function SectionListTracker({ items, onItemsVisible }: SectionListTrackerProps) {
  // Fragment ref resolves to a specialized FragmentInstance
  const fragmentRef = useRef<any>(null);

  useLayoutEffect(() => {
    if (!fragmentRef.current) return;

    const observer = new IntersectionObserver(
      (entries) => {
        const intersectingIds = entries
          .filter(entry => entry.isIntersecting)
          .map(entry => entry.target.getAttribute('data-id') ?? '')
          .filter(Boolean);

        if (intersectingIds.length > 0) {
          onItemsVisible(intersectingIds);
        }
      },
      { threshold: 0.6 }
    );

    // Observe all first-level DOM nodes in the Fragment without intermediate wrappers
    fragmentRef.current.observeUsing(observer);

    return () => {
      fragmentRef.current?.unobserveUsing(observer);
      observer.disconnect();
    };
  }, [onItemsVisible]);

  return (
    <dl className="semantic-description-list">
      {/* Direct ref assignment on Fragment */}
      <Fragment ref={fragmentRef}>
        {items.map((item) => (
          <Fragment key={item.id}>
            <dt data-id={item.id} className="item-title">
              {item.title}
            </dt>
            <dd className="item-description">
              {item.description}
            </dd>
          </Fragment>
        ))}
      </Fragment>
    </dl>
  );
}

The compressed version

Staff-level review starts with data flow, race conditions, and state boundaries — style comes last. Say exactly what an anti-pattern costs at runtime: syncing state via useEffect schedules a second render pass in the same frame and raises Total Blocking Time; computing the value during render removes that pass entirely. Explaining why manual useMemo/useCallback calls add risk without benefit under the Compiler lands better than just saying "remove these."

Push the architecture toward the primitives the runtime was built around: useActionState and discriminated unions instead of boolean flag soup, use(Promise) with Suspense instead of imperative fetching, native <form action={...}> instead of controlled input chains. And keep the escape hatches in view — "use no memo" for a mutating third-party library, use(browser()) for a client-only dependency — because knowing when not to reach for the new primitive is the other half of the judgment call.

Next step

Tell us what is broken or what should exist.

Send the shape of the problem and any constraints you already know — budget, deadline, the stack you are stuck with. You will get a written reply from the engineer who would do the work, not a sales sequence.