DevelopmentAll levels

React

React is a JavaScript library for building user interfaces out of components, each describing what the screen should show for the current state. React then works out how to change the page to match, and nearly every difficulty with it comes from breaking that model: impure work during render, state kept where it cannot be the source of truth, or an effect doing a job that belonged somewhere else. The page opens with every built-in hook on one screen and the four reasons a component renders, then covers render and commit, re-renders, state as a snapshot, effects, Server Components, and five documented cases.

Current version
19.3 · September 2026
Model
UI = f(state)
Governed by
React Foundation · 2026

React on one screen: every hook and what it is for

A React component is a function that returns a description of the screen, and hooks are the functions it calls to get anything else — state, a value from higher up the tree, a connection to the outside world. Here is every one of them first, because between them they are almost the whole of what you write in a component. The table lists every built-in hook react.dev documents as of React 19.3 (September 2026), in its groups, with the job each one was made for and the way each one gets misused.

HookWhat it is forHow it gets misused
useStateA value that belongs to this component and changes over timeStoring what could be computed from other state or props
useReducerState with several parts that change together, updated by one pure functionA whole application store rebuilt by hand in one component
useContextReading a value provided higher up without passing it through every levelA value that changes many times a second: every reader re-renders each time
useRefA value that survives renders without causing one — a DOM node, a timer idReading or writing it during render, where React cannot see the change
useImperativeHandleChoosing what a parent can call through a ref to your componentAn imperative API where an ordinary prop would do
useEffectKeeping the component in sync with a system outside React, after the screen updatesComputing values, or reacting to a click — the effects section below
useLayoutEffectMeasuring the page before the browser paints — a tooltip’s positionAnything that does not measure: it holds up the paint
useInsertionEffectCSS-in-JS libraries inserting styles before the DOM changesApplication code — it is not meant for you
useEffectEventReading the latest props inside an effect without making the effect re-run (19.2)Wrapping everything in it to silence the dependency linter
useMemoKeeping an expensive result between rendersWrapping cheap work, where the comparison costs more than the work
useCallbackThe same function between renders, for a memoised child or an effectEvery function, by default, as a ritual
useTransitionMarking an update as not urgent, so typing stays responsiveExpecting it to make slow code fast — it only reorders the work
useDeferredValueLetting a slow part of the screen lag behind a fast inputA debounce for network requests — it defers rendering, not fetching
useActionStateThe result of a form action and whether it is still runningA general-purpose reducer for state that has nothing to do with an action
useOptimisticShowing the expected result while an action is still in flightForgetting it reverts when the action fails — the failure needs a message
useReading a promise or a context — the only one allowed inside a conditionA promise created during render: a new one every time, so the component keeps suspending
useFormStatusWhether the form around this component is being submitted (react-dom)Calling it in the component that renders the <form> — it only sees a form above it
useIdA stable id that ties a label to its input, the same on server and clientKeys for a list — keys come from the data
useSyncExternalStoreSubscribing to a store React does not own — a library store, a browser APIWrapping state that could simply have been useState
useDebugValueA label for your own hook in React DevToolsExpensive formatting on every render — pass a formatter instead

Describe, do not instruct: render and commit

Before React, front-end code mostly gave instructions: find this node, set its text, add a class, remove that row. Every instruction needed a matching one that undid it, and the number of pairs grew faster than the features did — which is why old front-end code bases tended to break in places nobody had touched. React inverts that. You write a function that, given the current state, returns a description of the interface, and React works out the operations needed to match it. You never remove the row; you describe a list that no longer contains it. There is one description to get right instead of a growing set of transitions between states.

A state change makes React call your components again — that is the render phase, and it is pure: it produces a description of the UI and may be interrupted, discarded or repeated. Only the commit phase touches the DOM, and it happens once. Effects run after the commit. This is why a side effect written during render misbehaves and why the same code inside an effect does not.

The diagram splits the work into two phases, and the split is the most load-bearing idea on this page. The render phase calls your components to produce the description. It must be pure — the same inputs give the same output, and nothing outside the function changes — because React may run it twice, pause it halfway, or throw the result away. The commit phase applies the difference to the real DOM, once. Effects run after the commit, when the page is already up to date.

When a component re-renders

The four reasons from the top of the page produce one pattern in practice. When state changes in a component, that component renders, and so does everything below it, all the way down — whether or not the children receive anything that changed. Nothing above it renders. That is a deliberate default: calling a function and comparing its output is cheap, and a re-render is not a DOM update. It becomes expensive only when the subtree is large or a component in it does heavy work during render.

When state changes in Page, Page renders and so does everything it renders, down to the leaves — including Filters, whose props did not change. Nothing above Page renders. Two things stop the cascade: List is wrapped in memo and receives equal props, so React skips it and its rows; Sidebar was created by App and passed in as children, so it is the same element as last time and React skips it too. A child renders because its parent did, not because its props changed.

The diagram shows the three ways out, and they are worth trying in this order, because the first two are free and the third has a price. First, move the state down: if only one small subtree cares about a value, keep the state there and the rest of the page stops rendering. Second, pass pieces of the screen in as children: an element created by a parent that did not re-render is the same element as last time, and React skips it. Only then reach for memo, useMemo and useCallback. Each adds a comparison to every render, and each pays off only when the work it skips is genuinely larger than that comparison.

Since October 2025 a fourth option exists that changes the arithmetic. React Compiler 1.0 is a build step that reads your components and inserts the memoisation itself, more precisely than people write it by hand — it can memoise after an early return, which a hand-written useMemo cannot. At Meta it made initial loads and navigations in the Quest Store up to 12% faster and some interactions more than two and a half times faster. It has one condition: your code must follow the rules of React, render purity first among them. The compiler does not fix a component that breaks them; it skips it, and its lint rules tell you where.

Keys are identity, not decoration

One correctness rule hides inside the performance topic. When React renders a list, it uses each item’s key to decide which element on this render is the same as which element on the last one — and state, focus and the text half-typed into an input all belong to that identity. Using the array index as the key tells React that “the item in position two is the same item as before”, which stops being true the moment the list is sorted or an item is removed from the middle. The visible result is a text field or a checkbox that stays where it was while the data moves under it. Use a stable id from the data.

State: a snapshot, a queue and one source of truth

Inside one render, state does not change. count is a constant for the whole of that call to your function, and calling setCount does not touch it: it puts an update in a queue and asks React for another render. That is why three setCount(count + 1) calls in one click leave the counter at one, not three — each of them read the same count, which was zero, and each asked for one. Passing a function instead, setCount(c => c + 1), queues three steps that each receive the result of the one before.

Inside one render, state is a snapshot: count is 0 for the whole call to the component. setState does not change it; it queues an update for the next render. Three calls of setCount(count + 1) each read the same 0 and each ask for 1, so the next render shows 1. Three calls of setCount(c => c + 1) queue functions, and React passes each one the result of the one before, so the next render shows 3.

Since React 18 the queue is also batched everywhere: every update made during one event — in a handler, a timeout or after an await — is applied in a single render rather than one render each. And a queued value only causes a render if React considers it different, which it checks with Object.is: the same reference counts as unchanged. So pushing into an array that is already in state, or setting a field on an object that is already in state, hands React the same address and nothing happens on screen. The JavaScript page draws what a copy actually copies; for state, build a new object for every level you change.

The other half of state design is what deserves to be state at all. Anything you can compute from existing state or props during render should be computed, not stored. A fullName kept in state beside firstName and lastName is a third value that can disagree with the other two, and eventually will. The test is blunt: if two pieces of state can ever contradict each other, one of them is derived and should be deleted. Facebook’s chat team learned it the hard way and described it in 2014, with an unread count that kept showing messages nobody had sent — the first case below.

Kind of stateExampleWhere it belongs
Local UI stateAn open menu, the selected tabuseState in the lowest component that needs it
Derived valueA filtered list, a total, a full nameNowhere — compute it during render
Shared UI stateThe theme, the signed-in userLifted to the nearest common parent, or a context if it changes rarely
Server dataThe list of orders, the product pageA data library or the framework’s loader — it is a cache, not your state
URL stateThe search query, the page number, a filterThe URL, through the router — so it survives a reload and a shared link

Effects synchronise with the outside world

useEffect exists to keep a component in sync with something outside React: a subscription, a browser , a chart library that owns its own DOM node, a timer. That is its whole job. React’s own documentation has a page called “You Might Not Need an Effect”, which says a good deal about how often it is reached for by default. Most effects in a typical code base belong in one of the places on the right of this table.

You wrote an effect to…Do this instead
Compute a value when props changeCompute it during render. If it is genuinely expensive, useMemo — but measure first.
Reset state when a prop changesGive the component a key. A new key is a new component with fresh state, and it takes no code.
Send a request when a button is clickedDo it in the event handler. The cause is the click, not the render.
Tell a parent that state changedCall the callback where the change happens, or lift the state so there is nothing to tell.
Subscribe to a store or a browser APIuseSyncExternalStore, which handles subscribing, unsubscribing and consistency for you.
Fetch data when the screen opensA legitimate use, but prefer a data library or the framework’s loader — a bare effect has no cache and no cancellation, and it races.

When an effect is right, two parts of it need care. The first is the dependency array — the list of values that makes the effect run again when one of them changes. React compares each entry with Object.is, so an object or a function created during render is a new value every time, and an effect that depends on it runs after every single render. On a screen that renders often, an effect that calls an API becomes a loop that calls it constantly. That is exactly what took down Cloudflare’s dashboard in September 2025. Depend on primitive values — an id rather than the object that holds it — or keep the object stable with useMemo.

The second is the cleanup function, which is not optional. It runs before the effect runs again and when the component disappears, and it is where you unsubscribe, clear the timer and abort the request. An effect that subscribes without unsubscribing leaks a listener on every run — invisible in development, and the reason the tab is slow after twenty minutes. An effect that fetches without cleanup has a that your development machine rarely shows you:

A search box fetches results in an effect whenever the query changes. The user types “ky”, then “kyi” 150 ms later. The first request is slower and answers last. Without cleanup, whichever answer arrives last is shown, so the stale results for “ky” overwrite the correct ones for “kyi” at 700 ms. With cleanup, the effect for “ky” is cleaned up the moment the query changes, which marks its request stale or aborts it, so its late answer is ignored. The times are illustrative.

Where React runs now: frameworks and Server Components

For most of its life React was something you set up yourself, and Create React App was the standard way to start. In February 2025 the React team retired it and began recommending frameworks — Next.js, React Router, Expo for mobile — or a plain build tool such as for those who want to assemble the rest themselves. React is still a library, and that is a real trade rather than a slogan: routing, data loading, forms and the build are decisions you make and then own, and two React code bases in the same company can share almost no conventions. A framework makes those decisions for you, and brings one more with it: some of your components now run on a server.

Server ComponentClient ComponentServer Function
Marked withNothing — the default in a framework'use client' at the top of the file'use server'
RunsOn the server, before the page is sentOn the server for the first HTML, then in the browserOn the server, when the browser calls it
CanRead a database or a file directlyHold state, run effects, handle clicksChange data on the server from a form or a button
Ships code to the browserNoneAll of itNone — but it is a public endpoint

The gain from Server Components is mostly a bundle-size argument. A date-formatting library used only on the server costs the user nothing, and data loading moves next to the component that needs it, without a separate API in between. The line to hold is that anything interactive — onClick, state, an effect — lives in a Client Component, so the useful shape is a server tree with small client leaves. It is the shape this encyclopedia is built in: every article you read here is rendered by Server Components, and only small leaves — the menu, the search box, the table of contents, the table headers that stay put while you scroll — are Client Components.

Running on the server has a second cost: the server now runs React’s code for turning a request back into data, and that code can have bugs like any other. In December 2025 it had one rated 10 out of 10 — React2Shell, the fifth case below. And there is a seam where the two halves meet. The HTML the server sends must match what the browser renders when React takes the page over, a step called hydration; anything that differs between the two — the current time, a random number, the visitor’s time zone — produces a mismatch warning or a flicker. Put such values in an effect, or, since 19.3, behind browser(), so the server does not guess them.

Five cases, and what each one was missing

Each case below was described in public by the company it happened to, or by the React team. Three of them are about the model this page describes, and two are about what surrounds it — the licence and the server.

CaseWhat happenedWhat was missing
Facebook chat, 2014The unread count showed messages that did not exist, and came back after every fixOne source of truth, and data that flows one way
BSD+Patents, 2017Apache banned the licence and WordPress planned to leave; React moved to MIT within weeksA licence its users’ lawyers could accept
Create React App, 2025The standard starter for most React apps was retired with no maintainers leftAn owner for the setup everyone copied
Cloudflare, Sep 2025A dashboard effect re-ran on every render and overloaded the service that authorises every API callA stable dependency, and retries with random delays
React2Shell, Dec 2025A flaw in how Server Components read requests let anyone run code on the serverKnowing where React runs on a server, and a patch path measured in hours

The first case is where React’s way of thinking about state comes from. Facebook’s chat showed the number of unread messages in more than one place on the page, and each of those views kept and updated its own idea of what was unread. The count kept showing messages that did not exist. Engineers fixed it, and it came back with the next feature that touched chat, because any view could update any other and nobody could hold all the paths in their head. Jing Chen presented the answer at F8 in 2014: data flows in one direction, from a single store to every view, and a view changes the data only by sending an action back to the start. That is Flux, and it is the same rule as “if two pieces of state can contradict each other, one of them is derived”.

The second is a reminder that a library is also a legal agreement. React was published under a BSD licence with an extra patent clause: the grant ended for anyone who sued Facebook over patents. In July 2017 the Apache Software Foundation put that licence on its list of licences its projects may not depend on, and in September Matt Mullenweg announced that WordPress would move its new editor off React. On 22 September Facebook relicensed React under MIT, and React 16 shipped under it four days later. The code had not changed at all. What had been missing was a licence that the lawyers of the companies using it could sign off.

Where it degrades

A React code base rarely breaks in a way a test would catch. It gets slower, it shows a stale value for a moment, it renders twice for reasons nobody can name, and its effects slowly turn into a second program that runs beside the first. These are the shapes that takes.

  • Derived values stored in state, which can disagree with the values they were derived from.
  • Effects used to keep two pieces of state in sync — a calculation written a frame late.
  • An object or a function in a dependency array, so the effect runs after every render.
  • Fetching in a bare effect with no cleanup, which races and leaks.
  • The array index as a key, which attaches state and focus to the wrong row after a reorder.
  • Memoising everything by default, which adds a comparison to every render and hides the real cost.
  • Changing state in place, so React sees the same reference and skips the update.
  • Everything in one global store, which turns every change into an application-wide event.
  • Server Functions written as if only your own buttons could call them.

There is also an honest limit, and it is built into the one-line model at the top of the page. React guarantees that the screen matches the state. It says nothing about whether the state is right, whether the network answered, or whether the thing on the screen can be used. A <div onClick> rendered by the best-structured component tree in the world is still invisible to a keyboard and a screen reader, which the HTML and CSS page covers from the other side. And the library cannot make the page light: every Client Component is JavaScript the phone has to download, parse and run before the first click works.

The practical check is the same one that works for any code. Open the React DevTools profiler, turn on “highlight updates”, and do the one thing the screen exists for: every flash that is not the part you touched is a re-render you can name with the four reasons. Then do it again on a mid-range phone. The first test finds wasted work. The second finds whether anybody feels it.

When to use it

Use it when

  • The interface has genuine state that changes over time and in several places at once.
  • You want a component model and will take a framework, or choose routing, data and build tooling yourself.
  • The hiring pool matters — it is the largest one on the front end.
  • Server Components can move data loading and heavy libraries off the phone.

Avoid it when

  • A content page with a little interactivity — HTML with a small script is smaller and faster.
  • The team wants conventions supplied rather than chosen — that is what Angular offers instead.
  • Server Components on a team with no process for patching a server dependency the same day.
  • Adding it for one widget on an otherwise server-rendered page, where the runtime outweighs the widget.

Sources and method

What this page stands on

  • 12 sources
  • 2 documented failures
  • Checked October 13, 2026

Researched with Claude. Finding and cross-checking the primary sources was done with Claude (Anthropic). Selection, writing and fact-checking: the Anatomy of IT Product editors.

Found this useful?

Share it with someone who is working on the same problem.