Your first React component can be a counter, the smallest example that teaches the core loop: store a value in state, show it in the interface, and update it in an event handler. You will build it from scratch in a Vite project.
If you have not created a project yet, follow the guide on creating a React app with Vite. The counter goes in the App component inside src/App.jsx.
Start with a static button
Open src/App.jsx and replace its contents with a button. The component returns the button directly, with no state yet:
function App() {
return <button>Count: 0</button>;
}
export default App;Save the file and the browser shows a single button that says "Count: 0." Nothing happens when you click it because nothing changes yet.
Add state with useState
To make the number change, store it in state. Import the useState hook and call it at the top of the component:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return <button>Count: {count}</button>;
}
export default App;useState(0) creates a piece of state named count that starts at zero. setCount is the function that changes it. The curly braces inside the JSX insert the current value, so the button still shows "Count: 0." A plain variable would reset on every render, which is why state exists.
Update state on click
Add an onClick handler that increments the number:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
export default App;Click the button and the number goes up by one each time. The handler reads the current count, adds one, and hands the result to setCount.
React stores the new state, re-renders the component, and the button text updates to match. Click rapidly and the number still advances once per click.
Use a functional update
For the next value to be exact even when updates are queued, pass a function to setCount. Change the button inside App to this:
<button onClick={() => setCount((previous) => previous + 1)}>Count: {count}</button>The function receives the latest value and returns the next one. If two clicks are processed before React re-renders, each one adds to the newest number instead of to a stale copy.
The counter behaves the same on screen, but this is the safer habit whenever the new value depends on the old one.
The guide on useState goes deeper into state and why the functional form matters.
What to learn next
Now that a value lives in state and drives the screen, learn how React updates the page efficiently in the Virtual DOM guide. Then build more interactions with the events guide. Each new component follows the same pattern: state holds a value, JSX shows it, and an event handler changes it.
Rune AI
Key Insights
- useState stores a value that changes over time.
- JSX shows the current value inside curly braces.
- onClick runs a handler that calls setCount.
- React re-renders the component after every state update.
- Use a functional update when the next value depends on the previous one.
Frequently Asked Questions
Why does the counter use useState?
What does the count + 1 inside the click handler do?
Why use a functional update like setCount(previous => previous + 1)?
Conclusion
A counter is the smallest complete React component. Store the number in useState, show it in JSX, and update it with a click handler. The same three-step loop, state, render, and update, powers every React interface.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.