React
React is a JavaScript library for building user interfaces out of components — functions that take data and return a description of what should be on screen. It is not a framework: it renders UI and manages state, and leaves routing, data fetching, and build tooling to you or to a framework built on top of it.
The one idea
Section titled “The one idea”Everything in React follows from a single equation:
UI = f(state)Your view is a pure function of your data. You never write instructions to change the screen. You write a function that says what the screen should look like for a given state, then you change the state. React figures out what to do to the DOM.
This is why React apps are easier to reason about than hand-written DOM code: there is exactly one place that decides what the UI looks like, and it is a function you can read top to bottom.
Declarative vs imperative
Section titled “Declarative vs imperative”Imperative code describes how to get from the current screen to the next one. You have to know what is on screen already, and every new feature multiplies the number of transitions you have to handle by hand.
// Vanilla DOM: you mutate the page step by step.const button = document.querySelector('#inc');const output = document.querySelector('#count');let count = 0;
button.addEventListener('click', () => { count += 1; output.textContent = count; // must remember to sync this output.className = count > 9 ? 'big' : ''; // ...and this button.disabled = count >= 10; // ...and this});Declarative code describes what the screen should be, for any state. There are no transitions to keep in sync — only one description.
import { useState } from 'react';
function Counter() { const [count, setCount] = useState(0);
return ( <div> <span className={count > 9 ? 'big' : ''}>{count}</span> <button onClick={() => setCount(count + 1)} disabled={count >= 10}> Increment </button> </div> );}Add a feature to the imperative version and you edit every handler. Add a feature to the declarative version and you edit one description.
Components
Section titled “Components”A component is a function whose name starts with a capital letter and which returns JSX. That is the entire contract.
function Greeting({ name }) { return <h1>Hello, {name}</h1>;}
export default function App() { return ( <main> <Greeting name="Ada" /> <Greeting name="Grace" /> </main> );}Components compose like functions: a component’s output can contain other components, arbitrarily deep. The whole app is one tree of components with a single root.
Data flows down the tree through props, and events flow up through callbacks. This one-way flow is the second-biggest reason React apps stay understandable at scale.
A first taste of JSX
Section titled “A first taste of JSX”JSX is an extension to JavaScript syntax that lets you write markup inside code. It looks like HTML but it is JavaScript: every tag compiles to a function call.
const element = <h1 className="title">Hello</h1>;compiles (with the automatic JSX runtime, the default since React 17) to roughly:
import { jsx as _jsx } from 'react/jsx-runtime';const element = _jsx('h1', { className: 'title', children: 'Hello' });The result is a plain object — a React element. It is a lightweight description, not a DOM node:
// Roughly what a React element looks like{ type: 'h1', props: { className: 'title', children: 'Hello' }, key: null, ... }Because JSX is just JavaScript, anything in { } is an expression that gets evaluated:
const user = { name: 'Ada', admin: true };
const view = ( <div> <p>{user.name.toUpperCase()}</p> <p>{user.admin ? 'Administrator' : 'Member'}</p> <ul> {['a', 'b', 'c'].map((letter) => ( <li key={letter}>{letter}</li> ))} </ul> </div>);Full rules are on JSX and components. JSX is optional in principle — you can call jsx() yourself — but nobody does.
The virtual DOM and reconciliation
Section titled “The virtual DOM and reconciliation”If UI = f(state), then on every state change React could throw away the DOM and rebuild it. That would be correct but slow, and it would destroy focus, scroll position, and text selection.
Instead React keeps the element tree it produced last time. When state changes it:
- Re-renders — calls your component functions again, producing a new tree of element objects. This is cheap: it is plain object allocation, no DOM involved.
- Reconciles — walks the old and new trees together, comparing node by node.
- Commits — applies only the differences to the real DOM.
The in-memory element tree is what people call the virtual DOM. The comparison step is reconciliation, and it follows two rules that explain most React behaviour you will ever debug:
- Different type at the same position ⇒ destroy and rebuild. If a
<div>becomes a<span>, or<Profile>becomes<Settings>, React unmounts the old subtree (losing its state) and mounts the new one. - Same type at the same position ⇒ keep the DOM node and its state, update only changed attributes.
For lists, “position” alone is ambiguous, so you give each item a key. Keys tell React which item is which across renders. This is why key bugs show up as state attached to the wrong row — see rendering lists.
Setting up a project with Vite
Section titled “Setting up a project with Vite”Vite is the standard build tool for a client-side React app: instant dev server, hot module replacement, and an optimized production build.
npm create vite@latest my-app -- --template reactcd my-appnpm installnpm run devFor TypeScript, use the react-ts template instead:
npm create vite@latest my-app -- --template react-tsThe scaffold you get:
my-app/├── index.html # the real HTML page; loads /src/main.jsx as a module├── package.json├── vite.config.js└── src/ ├── main.jsx # entry point: mounts React into the page ├── App.jsx # your root component ├── App.css └── index.csssrc/main.jsx is the seam between the page and React:
import { StrictMode } from 'react';import { createRoot } from 'react-dom/client';import App from './App.jsx';import './index.css';
createRoot(document.getElementById('root')).render( <StrictMode> <App /> </StrictMode>);createRoot (React 18+) attaches a React root to a DOM node; everything React renders lives inside it. StrictMode is a development-only wrapper that intentionally double-invokes component bodies and effects to surface impure code and missing cleanup. It has no effect in production builds — see effects.
Useful scripts:
npm run dev # dev server with HMR, usually http://localhost:5173npm run build # production bundle into dist/npm run preview # serve dist/ locally to check the buildThe mental model
Section titled “The mental model”Five sentences that will carry you a long way:
- A component is a function of its props and state. Given the same inputs it must return the same output, with no side effects during rendering.
- Rendering is not painting. Calling your function produces a description; React decides separately what to change in the DOM.
- State changes cause re-renders. You never call a “redraw” function. You call a setter, React re-runs the affected components.
- Each render sees its own frozen snapshot of props and state. Values from an old render never change — this explains almost every “why is my variable stale?” bug.
- Data flows down, events flow up. If two components need the same data, move it to their closest common parent.
What React is not
Section titled “What React is not”React deliberately leaves out things a framework would include. You choose libraries for:
| Need | Common choices |
|---|---|
| Routing | React Router, TanStack Router, or a framework like Next.js |
| Server data & caching | TanStack Query, SWR, or a framework’s loaders |
| Global client state | Context, Zustand, Redux Toolkit, Jotai |
| Forms | React Hook Form, or plain controlled inputs |
| Styling | Plain CSS, CSS Modules, Tailwind, CSS-in-JS |
None of these are needed to learn React, and adding them early hides how React actually works.
How to read this section
Section titled “How to read this section”The pages build on each other in order:
- JSX and components — syntax, props, composition, lists.
- State and events —
useState, forms, the re-render model. - Effects and data —
useEffect, cleanup, fetching. - Hooks — refs, memoization, reducers, context, custom hooks.
- Patterns and performance — composition,
memo, common bugs. - TypeScript with React — typing props, hooks, and events.
React assumes real fluency in JavaScript — closures, array methods, destructuring, modules, and promises especially. If any of those are shaky, read JavaScript first.
Key points
Section titled “Key points”- React builds UIs from components: functions from data to a description of the screen.
UI = f(state): change state, never the DOM.- JSX compiles to function calls that produce plain element objects.
- Reconciliation diffs the old and new element trees and commits the minimum DOM change; type and
keydetermine what is kept. npm create vite@latest my-app -- --template reactis the fastest correct starting point.- Function components and hooks are modern React; class components are legacy.