React is a JavaScript library for building interactive user interfaces. You describe what the screen should look like for the current data, and React works out how to update the page when that data changes.
Why one button gets messy
A static page needs only HTML and CSS. The trouble starts when the page has to respond to the user. Take a like button that counts clicks and shows a message after five. In plain JavaScript you find each element and change it yourself:
const button = document.querySelector('#like');
const label = document.querySelector('#count');
const message = document.querySelector('#message');
let count = 0;
button.addEventListener('click', () => {
count += 1;
label.textContent = count;
if (count >= 5) {
message.hidden = false;
}
});That is fine for one button. Now add a cart badge that updates, a message that hides again and a colour that changes with the count. Every feature adds more code that edits the page by hand, and each piece has to know which other parts of the page depend on it. Miss one and the screen stops matching your data: the cart badge says three items while the list shows two. React was built to close that gap between data and screen.
Components: small, reusable pieces
React splits the page into components. A component is a JavaScript function that returns a description of a piece of UI. A button, a navbar, a product card and a whole checkout page can all be components, and bigger ones are built from smaller ones:
function Navbar() {
return (
<nav>
<Logo />
<CartButton itemCount={3} />
</nav>
);
}Component names start with a capital letter. That is how React tells <CartButton />, your component, apart from <button>, the built-in HTML element.
Components take inputs called props, passed like HTML attributes. CartButton receives itemCount and decides how to show it:
function CartButton({ itemCount }) {
return <button>Cart ({itemCount})</button>;
}Because a component is a function of its props, you can reuse it anywhere. The same CartButton works in the navbar, in a sidebar and on the checkout page, and a fix to it fixes all three.
State: a component's memory
Props come from the parent. State is data a component keeps for itself between renders: how many times a button was clicked, whether a menu is open, what is typed in a search box. In a function component you add state with the useState hook:
import { useState } from 'react';
function LikeButton() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Liked {count} times
</button>
{count >= 5 && <p>You really like this.</p>}
</div>
);
}useState(0) returns two things: the current value, count, and a function to change it, setCount. Calling setCount stores the new value and tells React the component needs to render again.
A plain variable can't do this job. If you wrote let count = 0 inside the function and added one to it on click, two things would go wrong. The variable starts again at 0 every time the function runs, and changing it tells React nothing, so the screen never updates. State solves both.
How React updates the screen
When state changes, React doesn't rebuild the whole page. It calls your component again to get its new output, compares that with the previous output, and changes only the parts of the real page that differ. The real page here is the DOM, the browser's live model of the page. This comparison step is called reconciliation.
Here is what happens when someone clicks a like button that already shows 7:
One click, from state change to screen update
Step 1 of 5: The button shows 7 likes, and you click it.
React splits that work into two phases. In the render phase it calls your components to find out what the UI should be, and nothing on screen changes yet. In the commit phase it applies the differences to the DOM. Rendering should be a pure calculation: given the same props and state, a component returns the same JSX and has no side effects, such as network requests, while doing it. That kind of work belongs in event handlers or in the useEffect hook.
Imperative and declarative code
Compare the two versions of the like button. The plain JavaScript one is imperative: it lists every step to change the page (find this element, set its text, unhide that one). The React one is declarative: it says what the UI should look like for a given count, and React handles the steps.
Put another way, the UI is a function of state. If count is 8, the button says 'Liked 8 times' and the message shows, every time, whatever happened before. You no longer track which DOM edits have already been made, so the cart badge and the cart list can't drift apart: both are drawn from the same state.
JSX: HTML-like syntax inside JavaScript
The markup in a React component is JSX. It looks like HTML, but it lives in your JavaScript files, so the UI sits next to the logic that drives it. Browsers can't run JSX directly: a build tool turns each tag into a plain function call. In the classic form, this:
<button className="primary">Save</button>becomes this:
React.createElement(
'button',
{ className: 'primary' },
'Save'
);Newer setups compile to a similar call imported from react/jsx-runtime, but the idea is the same: JSX is a shorter way to write function calls that describe elements.
Because it is JavaScript underneath, JSX has a few rules HTML doesn't:
- Curly braces drop a JavaScript expression into the markup:
{count},{user.name},{count >= 5 && <p>Wow</p>}. - Attributes use camelCase JavaScript names:
classNameinstead ofclass,onClickinstead ofonclick. - A component returns one root element. Wrap siblings in a
<div>or an empty fragment,<>...</>. - Every tag must be closed, including
<img />and<br />.
Mixing markup into JavaScript feels odd at first, especially if you were taught to keep HTML, CSS and JavaScript in separate files. The reasoning is that a button's markup and its click logic change together, so React keeps them in one component instead of splitting them by file type.
Why React is called a library
React does one job: turning state into UI and keeping the two in sync. It doesn't include a router, a way to fetch data or a build setup. You add those yourself, or use a framework built on React, such as Next.js, which adds routing and server rendering on top. That is why React is called a library: your app calls it when it needs UI, and the rest of the app's structure is up to you or your framework.
Common mistakes
Changing state directly
React only renders again when you call the setter with a new value. Editing an array or object in place keeps the same reference, so React may decide nothing changed and skip the update:
// Wrong: same array, React may not re-render
items.push(newItem);
setItems(items);
// Right: a new array
setItems([...items, newItem]);Expecting state to change straight away
Calling the setter doesn't change the count variable in the code that is already running. It schedules a new render, and the new value appears in that render:
function handleClick() {
setCount(count + 1);
setCount(count + 1);
// Both used the same count: it goes up by 1
}When the next value depends on the previous one, pass a function instead: setCount(c => c + 1) uses the latest value each time, so two calls add 2.
Using React where it isn't needed
A mostly static page, such as a blog post or a landing page with one form, gains little from React. It adds a JavaScript bundle to download and a build step to maintain. React pays off when a page has a lot of state that changes as the user works: dashboards, editors, shopping carts, chat.
Key takeaways
- React is a JavaScript library for building interactive user interfaces out of components.
- A component is a function that takes props and returns JSX, and small components combine into whole pages.
- State is a component's memory. Changing it with the setter makes React render again and update only what changed in the page.
- React is declarative: you describe the UI for the current state, not each step to redraw it.
- JSX looks like HTML but compiles to JavaScript function calls, so markup and logic live together.