Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

React User Guide

Generated May 2026 from facebook/react at 561ed52

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

React is a JavaScript library for building user interfaces using a declarative, component-based model. Developed and maintained by Meta, React lets you compose complex UIs from small, isolated pieces of code called components, and efficiently update the DOM when your data changes.

Key features:

  • Declarative component model with JSX syntax
  • Hooks API for state, effects, context, and more
  • Server-side rendering via react-dom/server
  • Concurrent rendering with automatic batching
  • React Server Components for streaming server-rendered content
  • React Compiler for automatic memoization (experimental)
  • Built-in Suspense for asynchronous data loading
  • React DevTools browser extension for debugging

Prerequisites: Node.js 14.17.0+ (Node 18+ recommended), npm or Yarn

Install and create your first app:

npx create-react-app my-app
cd my-app
npm start

Or with a framework (recommended for production):

npx create-next-app@latest my-app

Minimal example:

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

const root = createRoot(document.getElementById('root'));
root.render(<Counter />);

Tip

For production applications, use a framework like Next.js, Remix, or Gatsby that handles routing, server rendering, and build tooling out of the box.

What to try next:

  • Learn about hooks in the Core Usage section below
  • See Common Tasks for real-world recipes
  • Visit the Admin Guide for architecture internals and the full configuration reference

2. Installation

System Requirements

Requirement Minimum Recommended
Node.js 14.17.0 18+
Package Manager npm 6+ or Yarn 1.22+ npm 9+ or Yarn 1.22.22
Browser ES2015-compatible Chrome, Firefox, Safari, Edge (latest)

Installation Methods

npm

npm install react react-dom

Yarn

yarn add react react-dom

pnpm

pnpm add react react-dom

Important

Always install react and react-dom at the same version. Mismatched versions cause runtime errors like Invalid hook call or Cannot read properties of null.

For new projects, the React team recommends using a framework:

Framework Command Features
Next.js npx create-next-app@latest SSR, SSG, App Router, Server Components
Remix npx create-remix@latest Nested routing, server rendering, data loading
Gatsby npx create-gatsby Static site generation, GraphQL data layer
Expo (React Native) npx create-expo-app Mobile apps for iOS and Android

Manual Setup (Without a Framework)

If you need React without a framework, set up a bundler:

npm install react react-dom
npm install --save-dev @babel/core @babel/preset-react webpack webpack-cli babel-loader

Minimal webpack configuration:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react'],
          },
        },
      },
    ],
  },
};

Adding React to an Existing Project

Add React to a single page or section of an existing site:

<div id="react-root"></div>
<script type="module">
  import { createRoot } from 'react-dom/client';
  import App from './App.js';
  const root = createRoot(document.getElementById('react-root'));
  root.render(<App />);
</script>

Post-Install Verification

node -e "console.log(require('react').version)"
19.2.3

TypeScript Support

npm install --save-dev @types/react @types/react-dom typescript

In your tsconfig.json:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true
  }
}

Warning

When using TypeScript with React 19, ensure your @types/react version matches: use @types/react@19.2 with react@19.2.x. Older type definitions may cause compilation errors with new APIs like useActionState or useOptimistic.

Install Troubleshooting

Error: Invalid hook call

Cause: Multiple copies of React in the bundle, or mismatched react and react-dom versions.

Solution:

  1. Verify versions match: npm ls react react-dom
  2. Deduplicate: npm dedupe or add resolutions in package.json
  3. Ensure hooks are called at the top level of components, not inside conditions or loops

Prevention: Pin both packages to the same version in package.json.

Error: Cannot find module 'react'

Cause: React not installed, or node_modules corrupted.

Solution:

  1. Delete node_modules and lock file
  2. Run npm install again

Error: You might have more than one copy of React in the same app

Cause: A dependency bundles its own copy of React.

Solution:

{
  "resolutions": {
    "react": "19.2.3",
    "react-dom": "19.2.3"
  }
}

Error: Module not found: Can't resolve 'react-dom/client'

Cause: Using React 18+ API with a bundler that doesn't resolve package exports.

Solution: Update your bundler to a version that supports the exports field in package.json, or import from react-dom and use ReactDOM.createRoot().

3. Configuration

React itself requires minimal configuration. Most configuration happens at the framework or bundler level.

JSX Transform

Modern React (17+) uses the automatic JSX transform, eliminating the need to import React in every file:

{
  "presets": [
    ["@babel/preset-react", { "runtime": "automatic" }]
  ]
}

Development vs. Production

React includes extra warnings and checks in development mode. Production builds strip these for smaller bundle size and better performance.

Mode Bundle Size Warnings Performance
Development Larger Full validation, component stacks Slower (extra checks)
Production Smaller None Optimized

Set the environment for your bundler:

// webpack
new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production'),
});

Note

Frameworks like Next.js and Remix handle development/production mode automatically based on your build command (dev vs. build).

Strict Mode

Wrap your app in <StrictMode> to opt into additional development-time checks:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
);

StrictMode enables:

  • Double-invoking render functions and effects to find impure renders
  • Warnings for deprecated APIs
  • Warnings for unsafe lifecycle methods

React Compiler (Experimental)

React Compiler automatically optimizes your components by adding memoization. Install it as a Babel plugin:

npm install babel-plugin-react-compiler
{
  "plugins": ["babel-plugin-react-compiler"]
}

The companion ESLint plugin surfaces compilation issues:

npm install eslint-plugin-react-compiler
// eslint.config.js
import reactCompiler from 'eslint-plugin-react-compiler';

export default [
  reactCompiler.configs.recommended,
];

Tip

Run the React Compiler healthcheck to assess your codebase readiness: npx react-compiler-healthcheck --src "src/**/*.{js,jsx,ts,tsx}"

See the Admin Guide for the full configuration reference including all environment variables and build flags.

4. Core Usage

Components

Components are the building blocks of React applications. Define them as functions that return JSX:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

// Usage
<Greeting name="World" />

Component Types

Type Syntax Use Case
Function component function Foo() {} Standard — all new code
Class component class Foo extends Component {} Legacy — still supported
memo() memo(function Foo() {}) Skip re-render when props unchanged
lazy() lazy(() => import('./Foo')) Code-split, load on demand
forwardRef() forwardRef(function Foo(props, ref) {}) Expose a DOM node to parents

Built-in Components

Component Purpose
<Fragment> / <> Group elements without a wrapper DOM node
<StrictMode> Enable extra development checks
<Suspense> Show fallback while children load
<Profiler> Measure rendering performance
<Activity> Control component visibility (experimental)
<ViewTransition> Animate between views (experimental)

Hooks

Hooks let function components use state, effects, context, and other React features. All hooks must be called at the top level of your component — never inside conditions, loops, or nested functions.

Warning

Breaking the Rules of Hooks (calling hooks conditionally or in loops) causes unpredictable bugs. The eslint-plugin-react-hooks package catches these issues at lint time.

State Hooks

useState — Add local state to a component:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}

useReducer — Manage complex state with a reducer function:

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'decrement': return { count: state.count - 1 };
    default: throw new Error('Unknown action');
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

Effect Hooks

useEffect — Run side effects after render:

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    let cancelled = false;
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        if (!cancelled) setUser(data);
      });
    return () => { cancelled = true; };
  }, [userId]);

  if (!user) return <p>Loading...</p>;
  return <h1>{user.name}</h1>;
}

useLayoutEffect — Like useEffect, but fires synchronously after all DOM mutations. Use for measuring layout or synchronously re-rendering.

useInsertionEffect — Fires before any DOM mutations. Reserved for CSS-in-JS libraries that need to inject <style> tags.

Note

useEffect fires after the browser paints. If you need to measure DOM elements or prevent visual flickering, use useLayoutEffect instead.

Context Hooks

useContext — Read a context value:

import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Click me</button>;
}

// Provider wraps the tree
<ThemeContext.Provider value="dark">
  <ThemedButton />
</ThemeContext.Provider>

Ref Hooks

useRef — Hold a mutable value that persists across renders without causing re-renders:

import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  return (
    <>
      <input ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </>
  );
}

useImperativeHandle — Customize the value exposed via ref to parent components.

Performance Hooks

useMemo — Cache an expensive computation:

const sortedItems = useMemo(() => {
  return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);

useCallback — Cache a function definition:

const handleClick = useCallback(() => {
  console.log('Clicked:', id);
}, [id]);

useDeferredValue — Defer updating a value to keep the UI responsive:

const deferredQuery = useDeferredValue(query);

useTransition — Mark a state update as non-urgent:

const [isPending, startTransition] = useTransition();

function handleSearch(input) {
  startTransition(() => {
    setSearchResults(filterResults(input));
  });
}

Tip

With React Compiler enabled, manual useMemo and useCallback calls become unnecessary — the compiler adds memoization automatically.

Form Hooks (React 19+)

useActionState — Manage form action state with pending status:

import { useActionState } from 'react';

async function submitForm(prevState, formData) {
  const result = await saveToServer(formData);
  return { message: result.message };
}

function MyForm() {
  const [state, formAction, isPending] = useActionState(submitForm, { message: '' });
  return (
    <form action={formAction}>
      <input name="email" />
      <button disabled={isPending}>Submit</button>
      <p>{state.message}</p>
    </form>
  );
}

useFormStatus — Read the status of a parent <form>:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button disabled={pending}>Submit</button>;
}

useOptimistic — Show optimistic UI while an async action completes:

import { useOptimistic } from 'react';

function TodoList({ todos, addTodo }) {
  const [optimisticTodos, addOptimistic] = useOptimistic(
    todos,
    (state, newTodo) => [...state, { ...newTodo, sending: true }]
  );

  async function handleAdd(formData) {
    const newTodo = { text: formData.get('text') };
    addOptimistic(newTodo);
    await addTodo(newTodo);
  }

  return (
    <form action={handleAdd}>
      <input name="text" />
      <button>Add</button>
      <ul>
        {optimisticTodos.map(todo => (
          <li key={todo.id} style={{ opacity: todo.sending ? 0.5 : 1 }}>
            {todo.text}
          </li>
        ))}
      </ul>
    </form>
  );
}

Other Hooks

Hook Purpose
useId Generate unique IDs for accessibility attributes
useDebugValue Display a label in React DevTools for custom hooks
useSyncExternalStore Subscribe to an external store (Redux, Zustand, etc.)
use Read a promise or context value (React 19+)
useEffectEvent Capture the latest values without re-running effects (experimental)

JSX Syntax

JSX is a syntax extension that lets you write HTML-like markup in JavaScript:

const element = <h1 className="title">Hello, world!</h1>;

Key rules:

  • Use className instead of class
  • Use htmlFor instead of for
  • Self-close tags without children: <img src="..." />
  • Wrap multiple elements in a fragment: <>...</>
  • Embed expressions with curly braces: {expression}
  • Style objects use camelCase: style={{ backgroundColor: 'red' }}

Rendering

Client Rendering

import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Hydrating Server-Rendered HTML

import { hydrateRoot } from 'react-dom/client';

hydrateRoot(document.getElementById('root'), <App />);

Portals

Render children into a different DOM node:

import { createPortal } from 'react-dom';

function Modal({ children }) {
  return createPortal(children, document.getElementById('modal-root'));
}

Server Rendering

React supports multiple server rendering strategies:

API Environment Stream Type Use Case
renderToPipeableStream Node.js Node stream SSR with streaming and Suspense
renderToReadableStream Edge/Browser/Bun Web stream Edge runtime SSR
renderToString Any String Legacy SSR (no streaming)
renderToStaticMarkup Any String Static HTML without React attributes
prerender Any Static Pre-render for static generation
import { renderToPipeableStream } from 'react-dom/server';

app.get('/', (req, res) => {
  const { pipe } = renderToPipeableStream(<App />, {
    bootstrapScripts: ['/client.js'],
    onShellReady() {
      res.setHeader('Content-Type', 'text/html');
      pipe(res);
    },
  });
});

Important

renderToString does not support Suspense or streaming. For new server rendering implementations, use renderToPipeableStream (Node.js) or renderToReadableStream (edge runtimes).

React Server Components

Server Components run only on the server, allowing direct data access without client-side JavaScript:

// This component runs on the server only
async function BlogPost({ slug }) {
  const post = await db.posts.findBySlug(slug);
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  );
}

Client Components are marked with 'use client':

'use client';

import { useState } from 'react';

export function LikeButton() {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(true)}>{liked ? '❤' : '♡'}</button>;
}

Server Actions are marked with 'use server':

'use server';

export async function addComment(formData) {
  const comment = formData.get('comment');
  await db.comments.create({ text: comment });
}

5. Common Tasks

Create a Custom Hook

Extract reusable stateful logic into a custom hook:

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored !== null ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// Usage
function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>{theme}</button>;
}

Fetch Data with Suspense

import { Suspense, use } from 'react';

function fetchUser(id) {
  return fetch(`/api/users/${id}`).then(r => r.json());
}

function UserProfile({ userPromise }) {
  const user = use(userPromise);
  return <h1>{user.name}</h1>;
}

function App() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
      <UserProfile userPromise={fetchUser(1)} />
    </Suspense>
  );
}

Handle Form Submissions (React 19+)

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

async function createUser(prevState, formData) {
  const res = await fetch('/api/users', {
    method: 'POST',
    body: formData,
  });
  if (!res.ok) return { error: 'Failed to create user' };
  return { success: true };
}

function SubmitButton() {
  const { pending } = useFormStatus();
  return <button disabled={pending}>{pending ? 'Creating...' : 'Create User'}</button>;
}

function CreateUserForm() {
  const [state, formAction] = useActionState(createUser, {});
  return (
    <form action={formAction}>
      <input name="name" required />
      <input name="email" type="email" required />
      <SubmitButton />
      {state.error && <p className="error">{state.error}</p>}
      {state.success && <p>User created!</p>}
    </form>
  );
}

Code-Split with Lazy Loading

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

function App({ page }) {
  return (
    <Suspense fallback={<p>Loading page...</p>}>
      {page === 'dashboard' ? <Dashboard /> : <Settings />}
    </Suspense>
  );
}

Share State with Context

import { createContext, useContext, useState } from 'react';

const AuthContext = createContext(null);

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  return (
    <AuthContext.Provider value={{ user, login: setUser, logout: () => setUser(null) }}>
      {children}
    </AuthContext.Provider>
  );
}

function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}

function Profile() {
  const { user, logout } = useAuth();
  if (!user) return <p>Not logged in</p>;
  return <button onClick={logout}>Logout {user.name}</button>;
}

Manage Lists with Keys

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Warning

Never use array indices as keys when the list can be reordered, filtered, or items inserted. Use stable, unique identifiers from your data. Index keys cause subtle bugs with component state.

Integrate with External Libraries

Use useRef and useEffect to integrate non-React libraries:

import { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';

function Map({ center, zoom }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);

  useEffect(() => {
    mapRef.current = new mapboxgl.Map({
      container: containerRef.current,
      center,
      zoom,
    });
    return () => mapRef.current.remove();
  }, []);

  useEffect(() => {
    mapRef.current?.setCenter(center);
  }, [center]);

  return <div ref={containerRef} style={{ width: '100%', height: 400 }} />;
}

Measure Performance with Profiler

import { Profiler } from 'react';

function onRender(id, phase, actualDuration) {
  console.log(`${id} ${phase}: ${actualDuration.toFixed(1)}ms`);
}

function App() {
  return (
    <Profiler id="App" onRender={onRender}>
      <Dashboard />
    </Profiler>
  );
}

Test Components

import { act } from 'react';
import { createRoot } from 'react-dom/client';

let container;
beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  container.remove();
});

test('renders greeting', () => {
  act(() => {
    createRoot(container).render(<Greeting name="Alice" />);
  });
  expect(container.textContent).toBe('Hello, Alice!');
});

Use React with TypeScript

import { useState, type ReactNode } from 'react';

interface CardProps {
  title: string;
  children: ReactNode;
}

function Card({ title, children }: CardProps) {
  const [expanded, setExpanded] = useState(false);
  return (
    <div>
      <h2 onClick={() => setExpanded(!expanded)}>{title}</h2>
      {expanded && <div>{children}</div>}
    </div>
  );
}

Preload Resources

React DOM provides APIs for resource preloading:

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function App() {
  prefetchDNS('https://api.example.com');
  preconnect('https://cdn.example.com');
  preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2' });
  preinit('/analytics.js', { as: 'script' });

  return <div>...</div>;
}

6. Troubleshooting

Diagnostic Decision Tree

Problem with React app?
├── Build/compile error?
│   ├── Missing module → Check installation (Section 2)
│   ├── JSX syntax error → Check Babel/TypeScript config (Section 3)
│   └── Type error → Check @types/react version matches react version
├── Runtime error in console?
│   ├── "Invalid hook call" → Check Rules of Hooks / duplicate React
│   ├── "Objects are not valid as a React child" → Stringify or extract values
│   ├── "Rendered more hooks than during the previous render" → Conditional hook call
│   └── "Cannot update state on unmounted component" → Missing cleanup in useEffect
├── Component not rendering?
│   ├── No root created → Verify createRoot() call
│   ├── State not updating → Check useState/useReducer usage
│   └── Stale closure → Add missing dependency to useEffect array
└── Performance issue?
    ├── Excessive re-renders → Use React DevTools Profiler
    ├── Large bundle → Code-split with lazy()
    └── Slow initial load → Enable SSR or prerendering

Error: Invalid hook call. Hooks can only be called inside of the body of a function component

Cause: One of: (1) calling a hook inside a condition, loop, or nested function; (2) multiple copies of React in the bundle; (3) calling a hook from a regular (non-component, non-hook) function.

Solution:

  1. Verify hooks are at the top level of your component
  2. Run npm ls react to check for duplicate installations
  3. Add eslint-plugin-react-hooks to catch violations at lint time

Prevention: Install the ESLint plugin and configure the rules-of-hooks rule as error.

Error: Rendered more hooks than during the previous render

Cause: A hook is called conditionally, causing the number of hook calls to differ between renders.

Solution:

// WRONG: conditional hook
function Foo({ showExtra }) {
  if (showExtra) {
    useEffect(() => { /* ... */ }); // This breaks
  }
}

// CORRECT: call the hook unconditionally
function Foo({ showExtra }) {
  useEffect(() => {
    if (showExtra) { /* ... */ }
  }, [showExtra]);
}

Error: Objects are not valid as a React child

Cause: Attempting to render a plain object or Date directly in JSX.

Solution: Convert the value to a string: {JSON.stringify(obj)}, {date.toLocaleDateString()}, or extract specific fields: {user.name}.

Error: Each child in a list should have a unique "key" prop

Cause: Rendering a list without providing a key prop, or using duplicate keys.

Solution:

{items.map(item => <li key={item.id}>{item.name}</li>)}

Prevention: Always use a stable, unique identifier from your data as the key.

Error: Maximum update depth exceeded

Cause: Calling setState inside useEffect without proper dependencies, creating an infinite render loop.

Solution: Ensure your useEffect dependency array is correct and that state updates are conditional:

useEffect(() => {
  if (data !== prevData) {
    setProcessedData(transform(data));
  }
}, [data]);

Error: Cannot update a component while rendering a different component

Cause: Calling setState of one component during the render phase of another.

Solution: Move the state update into a useEffect or an event handler.

Error: Hydration failed because the server-rendered HTML didn't match the client

Cause: The server and client rendered different content. Common causes include browser-only APIs (window, localStorage), timestamps, or random values.

Solution:

  1. Move browser-only logic into useEffect
  2. Use suppressHydrationWarning for intentional mismatches (e.g., timestamps)
  3. Ensure server and client render identical initial content

Warning: Can't perform a React state update on an unmounted component

Cause: An async operation (fetch, timer) completed after the component unmounted.

Solution: Return a cleanup function from useEffect:

useEffect(() => {
  const controller = new AbortController();
  fetch(url, { signal: controller.signal }).then(/* ... */);
  return () => controller.abort();
}, [url]);

Caution

While React 19 removed this warning for most cases, leaked subscriptions and timers can still cause memory leaks. Always clean up in the useEffect return function.

Performance: Components re-rendering too often

Cause: Parent re-renders cause all children to re-render, even when their props haven't changed.

Solution:

  1. Wrap child components with React.memo()
  2. Memoize expensive computations with useMemo
  3. Memoize callback props with useCallback
  4. Use the React DevTools Profiler to identify which components re-render and why
  5. Consider enabling React Compiler for automatic memoization

Issue: renderToPipeableStream doesn't start streaming (GitHub #33352)

Cause: When the first child node is a Fragment wrapping a Suspense boundary, streaming may not initiate until the Suspense promise resolves.

Solution: Avoid wrapping the first rendered node in a Fragment, or ensure the outermost Suspense boundary has non-suspended content.

Issue: useEffect does not re-fire on hot reload with React 19 (GitHub #29915)

Cause: React 19's effect cleanup behavior interacts differently with Vite's hot module replacement.

Solution: This was resolved in later React 19 patches. Update to the latest React 19 release.

Warning

React DevTools injects installHook.js into pages and may attempt to download its own source maps (GitHub #32339). This is cosmetic and does not affect application behavior, but may appear in network logs.

7. FAQ

Q: What version of React should I use?

Use the latest stable release (currently React 19.x). React 19 includes Server Components, Actions, useActionState, useOptimistic, the use hook, and other improvements. React 18 is still supported but will eventually stop receiving updates.

Q: Do I need a framework to use React?

No, but the React team recommends it for most applications. Frameworks like Next.js provide routing, server rendering, and build tooling that you would otherwise need to configure manually. For adding React to a small part of an existing page, you can use React without a framework.

Q: What is the difference between useState and useReducer?

useState is best for simple, independent state values. useReducer is better for complex state logic where the next state depends on the previous state, or when multiple state updates are related. useReducer also makes it easier to test state transitions in isolation.

Q: When should I use useEffect vs. useLayoutEffect?

Use useEffect for most side effects (data fetching, subscriptions, logging). Use useLayoutEffect only when you need to measure DOM layout or make synchronous visual updates before the browser paints — for example, positioning a tooltip based on element dimensions.

Q: Is forwardRef still needed in React 19?

In React 19, ref is available as a regular prop on function components, so forwardRef is no longer required for new code. It remains supported for backward compatibility.

Q: How do I optimize a large React application?

Start with the React DevTools Profiler to identify bottlenecks. Then apply targeted optimizations: code-split with lazy(), memoize with memo()/useMemo/useCallback, use useDeferredValue for non-urgent updates, and enable React Compiler for automatic memoization. See the Admin Guide for architectural performance strategies.

Q: What are Server Components?

Server Components run exclusively on the server, producing HTML that gets streamed to the client. They can access server-side resources directly (databases, file system) without creating API endpoints. Client Components (marked with 'use client') handle interactivity on the browser. A framework like Next.js App Router manages the Server/Client Component boundary automatically.

Q: How do I migrate from class components to function components?

Replace this.state with useState, componentDidMount/componentDidUpdate with useEffect, this.context with useContext, and componentWillUnmount cleanup with the useEffect return function. Convert one component at a time — class and function components can coexist.

Q: What is flushSync and when should I use it?

flushSync forces React to flush pending state updates synchronously. Use it when you need the DOM to update immediately — for example, to scroll to a newly added list item. Avoid it in most cases, as it defeats automatic batching and can hurt performance.

Q: How do I use React with Web Components?

Render Web Components in JSX like any other HTML element. Pass data via attributes (strings) or use ref to set properties and add event listeners programmatically.