React
In short: A JavaScript library for building user interfaces, developed by Meta — the foundation Next.js is also built on.
In more detail: React describes the UI as a tree of reusable components and, on state changes, efficiently updates only the affected parts of the page (a virtual DOM instead of a complete re-render). It’s deliberately just a “view” library — routing, data loading, etc. are handled by frameworks like Next.js that build on top of React.
In Depth
Origin and basic idea
React was developed internally at Facebook starting in 2011 (among other things for the news feed and for Instagram.com) and released as open source in 2013. The trigger was a recurring problem with classic web applications: as soon as data changes in several places on the page at once (e.g. a like counter AND a notification badge AND a comment list), the manual DOM code keeping all these places in sync quickly becomes unwieldy and error-prone — it’s easy to forget to update one place when making a small change elsewhere. React’s answer: instead of manually maintaining DOM state, you only describe what the UI SHOULD look like for a given data state, and React itself computes the necessary difference from the current DOM.
Declarative instead of imperative
// Classic (imperative) - you describe STEPS
const badge = document.getElementById("badge");
badge.textContent = unreadMessages;
badge.style.display = unreadMessages > 0 ? "block" : "none";
// React (declarative) - you describe the RESULT
function Badge({ count }) {
if (count === 0) return null;
return <span className="badge">{count}</span>;
}On every state change, React calls the component function again and compares the newly computed virtual DOM (a lightweight JavaScript object describing the desired UI tree) with the previous one — this diffing algorithm (“reconciliation”) determines the minimal set of actual DOM operations and only performs those, instead of completely re-rendering the page.
Hooks: state in function components
function Counter() {
const [value, setValue] = useState(0);
useEffect(() => {
document.title = `Counter: ${value}`;
}, [value]); // only reruns when "value" changes
return <button onClick={() => setValue(value + 1)}>{value}</button>;
}Before React 16.8 (2019), you needed class components with lifecycle methods like componentDidMount/componentDidUpdate for internal state. Hooks (useState, useEffect, useContext, among others) replace this with simple function calls inside function components, and have since become the standard way — class components are hardly written in new code any more, but remain supported for compatibility reasons.
Deliberately just a view library
React itself deliberately only provides the UI layer — no routing, no loading data from the server, no state management across several pages. This fundamentally distinguishes React from “batteries-included” frameworks like Angular (comes with routing, an HTTP client, dependency injection and much more built in) or even Vue in its official full setup. Instead, an ecosystem of complementary libraries and meta-frameworks emerged: Next.js (routing, server rendering, API routes), React Router (pure client-side routing), Redux/Zustand/Jotai (global state), React Query/SWR (server-data caching). This modularity is both React’s greatest strength (free choice of tools) and its biggest barrier to entry (many decisions needed before an application even runs) — one reason many new projects start directly with a meta-framework like Next.js instead of “plain” React.
Common pitfalls
A common beginner mistake is directly mutating state (state.push(x) instead of setState([...state, x])) — React detects changes via reference comparison, and a mutated array/object keeps the same reference and therefore doesn’t trigger a re-render. A second classic mistake is forgetting the dependency array in useEffect, which can lead to infinite loops or stale values in closures.
See also: React Native, JS, Angular, Vue, jQuery