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
reactandreact-domat the same version. Mismatched versions cause runtime errors likeInvalid hook callorCannot read properties of null.
Framework Setup (Recommended)
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/reactversion matches: use@types/react@19.2withreact@19.2.x. Older type definitions may cause compilation errors with new APIs likeuseActionStateoruseOptimistic.
Install Troubleshooting
Error: Invalid hook call
Cause: Multiple copies of React in the bundle, or mismatched react and react-dom versions.
Solution:
- Verify versions match:
npm ls react react-dom - Deduplicate:
npm dedupeor add resolutions inpackage.json - 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:
- Delete
node_modulesand lock file - Run
npm installagain
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 (
devvs.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-hookspackage 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
useEffectfires after the browser paints. If you need to measure DOM elements or prevent visual flickering, useuseLayoutEffectinstead.
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
useMemoanduseCallbackcalls 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
classNameinstead ofclass - Use
htmlForinstead offor - 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
renderToStringdoes not support Suspense or streaming. For new server rendering implementations, userenderToPipeableStream(Node.js) orrenderToReadableStream(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:
- Verify hooks are at the top level of your component
- Run
npm ls reactto check for duplicate installations - Add
eslint-plugin-react-hooksto 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:
- Move browser-only logic into
useEffect - Use
suppressHydrationWarningfor intentional mismatches (e.g., timestamps) - 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
useEffectreturn 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:
- Wrap child components with
React.memo() - Memoize expensive computations with
useMemo - Memoize callback props with
useCallback - Use the React DevTools Profiler to identify which components re-render and why
- 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.jsinto 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.