In one sentence
React is a JavaScript library for building user interfaces by breaking them down into self-contained, reusable pieces of code called "components" that automatically update when their data changes.
The problem it solves
Picture web development in the old days (say, the early 2010s). You had a webpage, and you wanted to make it interactive. Maybe you wanted a "like" button to turn blue and update a counter when clicked. Using a tool like jQuery, you'd write code that said: "find the button in the page, listen for a click, and when it happens, find the counter element and change its text, then find the button icon and change its color."
This is an imperative approach. You're giving the browser a step-by-step recipe for how to change the page. It works for a like button. But what about a full social media feed with hundreds of posts, comments, notifications, and a live chat window, all updating at once? The "recipes" become a tangled mess of spaghetti code. A change in one place could unpredictably break something else entirely. Keeping the UI consistent with the application's data was a constant, error-prone battle.
Facebook was living this nightmare at scale. In 2011, they created React to solve this problem internally. React introduced a declarative model. Instead of telling the browser how to change things, you just describe what the UI should look like for any given state. You declare: "If the post is liked, the button should be blue and the count should be 101. If it's not liked, the button is gray and the count is 100." That's it. When the liked state changes from false to true, React takes care of figuring out the most efficient way to update the button and the counter. It handles the "how," so you can focus on the "what."
This shift from imperative to declarative thinking was a revolution. It made building complex, data-driven UIs more predictable, scalable, and easier to debug.
How it works under the hood
React's "magic" isn't really magic; it's a clever combination of three core ideas: JSX, Components with State, and the Virtual DOM.
JSX: Sugar-Coated JavaScript
When you look at React code, you see something that looks a lot like HTML embedded directly in JavaScript. This is JSX (JavaScript XML).
const greeting = <h1>Hello, FlowingDev user!</h1>;
Your browser has no idea what to do with this. It's not valid JavaScript. Before this code can run, it needs to be "transpiled" (transformed) by a tool like Babel. That JSX line above gets converted into a regular JavaScript function call.
const greeting = React.createElement("h1", null, "Hello, FlowingDev user!");
Every JSX tag becomes a React.createElement() call. This function call doesn't create an actual HTML element. It creates a lightweight JavaScript object that describes the element you want to create—its type (h1), its properties (none, in this case, so null), and its children ("Hello..."). These objects are the building blocks of the Virtual DOM.
So, JSX is just "syntactic sugar." It lets us describe our UI with a familiar, HTML-like syntax, but under the hood, we're just creating a tree of JavaScript objects.
Components and State: The Brains of the Operation
A component is a JavaScript function that returns some JSX. It's a reusable, self-contained piece of UI.
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
This Greeting component is simple. It takes a name as an input (called a "prop") and renders a heading. But what if we want a component to remember things and change over time, like a counter? For that, we need "state."
State is data that a component "owns" and can change. React provides special functions called "hooks" to manage this. The most common one is useState.
import { useState } from 'react';
function Counter() {
// "count" is our state variable.
// "setCount" is the *only* function we can use to update it.
const [count, setCount] = useState(0); // The initial value is 0.
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
When you call setCount(count + 1), you're not just changing a variable. You're telling React: "Hey, the state for this Counter component has changed. Please run this function again to get the new UI, and then update the screen." React then "re-renders" the component, gets the new JSX with the updated count, and efficiently updates the webpage.
The Virtual DOM: React's Secret Weapon
Directly changing the real browser DOM (the live tree of HTML elements) is slow. It's like redecorating a room by tearing down all the walls and rebuilding them, even if you only wanted to move a picture frame.
React avoids this expensive work with its secret weapon: the Virtual DOM (VDOM). The VDOM is just a copy of the DOM kept in JavaScript memory. It's that tree of React.createElement objects we talked about.
Here's the process, known as reconciliation:
- Initial Render: When
Counterfirst renders, React builds a VDOM tree that describes adivcontaining apwith the text "You clicked 0 times" and abutton. It then uses this to create the real DOM elements on the page. - State Change: You click the button.
setCount(1)is called. - Re-render and New VDOM: React re-runs the
Counterfunction. It builds a new VDOM tree. This new tree is almost identical to the old one, but thepnow describes the text "You clicked 1 times". - Diffing: React's "diffing" algorithm compares the new VDOM tree with the old one. It zips through them and finds the differences. In this case, it finds only one difference: the text inside the
<p>tag has changed. - Batch Update: React takes this one tiny change and applies it to the real DOM. It doesn't touch the
divor thebutton; it just updates the text of thep.
This diff-and-patch process is incredibly fast. By batching updates and minimizing direct manipulation of the slow, real DOM, React makes UIs feel snappy and responsive, even when complex data is changing rapidly.
Real-world stories
The Case of the Disappearing Form Data
A junior dev was tasked with building a complex multi-step user registration form. Each step was a different component. At one point, they needed a small checkbox in Step 2 to conditionally show an extra input field. They implemented it, but suddenly, they had a bizarre bug: whenever a user checked that box, all the data they had entered in Step 2 would vanish.
The culprit was a misunderstanding of "keys." To show the extra field, the developer was changing the structure of their JSX in a way that made React think the entire form component for Step 2 was a new and different component. Without a stable key prop to identify it, React destroyed the old component (along with all its state, i.e., the user's input) and mounted a brand-new one in its place.
The lesson: The key prop isn't just for lists. It's a stable identity for a component. Understanding how React uses keys to track components across renders is crucial for preserving state and avoiding unexpected resets.
The Infinite API Hammer
A team was building a dashboard that displayed live stock data. A developer wrote a component to fetch the latest price for a specific stock using the useEffect hook. The code looked something like this:
useEffect(() => {
fetchStockPrice(stockSymbol).then(price => {
setPrice(price); // Update the state with the new price
});
}); // Whoops, no dependency array!
As soon as the component loaded, the app froze, the fan on the developer's laptop spun up, and network monitoring showed thousands of identical API requests being fired off per second. They had created an infinite loop. The useEffect fetched data and called setPrice. The state update caused the component to re-render. Because the useEffect had no dependency array, it ran again after every render, fetching the data again, updating the state again, causing another re-render, and on and on forever.
The lesson: React hooks like useEffect are powerful but precise tools. You must provide a "dependency array" to tell React when to re-run the effect. Leaving it out means "run after every single render," which is a recipe for disaster with state updates.
Common mistakes and traps
- Mutating state directly. Never do
myState.prop = 'value'ormyArray.push('item'). React's change detection relies on object and array identities. When you callsetState, you must provide a new object or array (e.g., using the spread syntax...) for React to notice the change. - Forgetting
keys in lists. When you render a list of items using.map(), you must give each element a unique and stablekeyprop (likeitem.id). This is how React's diffing algorithm can efficiently track which items have moved, been added, or been removed. Using the array index as a key is a common anti-pattern that can lead to bugs. - Treating JSX like HTML. It's close, but not the same. You have to use
classNameinstead ofclass,htmlForinstead offor, and camelCase for style properties (backgroundColornotbackground-color). Also, all tags must be closed, so an image is<img ... />, not<img>. - Thinking hooks are magic. Hooks like
useStateanduseEffectfollow strict rules (e.g., they can only be called at the top level of a function component). They work by relying on a consistent call order between renders. Trying to call them inside loops or conditions will break your app.
Why it belongs on your radar
React isn't just a library; it's an ecosystem and a way of thinking. It powers a huge portion of the modern web, from massive sites like Instagram and Netflix to countless startups and internal tools. Learning React means learning a component-based architecture that promotes reusable, testable, and maintainable code.
Even if you don't use React daily, understanding its core principles—declarative UI, state management, and the VDOM—will make you a better developer. These concepts have influenced almost every other modern UI framework, from Vue to Svelte. Furthermore, with React Native, the same skills and principles can be used to build native mobile apps for iOS and Android. It's a powerful paradigm that's essential for any developer working on user interfaces today.
Go deeper
- React Docs (Official) - The official documentation is the best place to start, with a comprehensive tutorial and deep dives.
- Introducing JSX (React Docs) - The official explanation of JSX and how it differs from HTML.
- Reconciliation (React Docs) - The algorithm React uses to diff two VDOM trees and update the UI efficiently.
- The Rules of Hooks (React Docs) - The essential rules you must follow when using hooks like
useStateanduseEffect. - MDN: Introduction to the DOM - To understand what React is manipulating, it's vital to understand the Document Object Model itself.
How an editor understands code
Syntax colors come from a language parser, not from executing the program. CodeMirror and Lezer recognize tokens and grammatical structure locally in the browser. Suggestions combine language keywords, local names where supported, snippets and a small built-in reference; hovering a documented symbol reveals its signature. A suggestion is a possibility, not proof that an API exists in the runtime. Parser diagnostics catch malformed syntax where the parser supports it, but do not replace type checking or execution: valid syntax can still fail. Ruby and Lua use token-based modes, so their editor diagnostics are more limited. There is no remote language server, and source code never needs to leave the device.
CodeMirror & Lezer: MIT.