Your First React Component: Build a Counter from Scratch

Build your first React component, a counter, from scratch. Learn how useState and onClick work together to keep the screen in sync with a number.

5 min read

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:

App.jsxApp.jsx
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:

App.jsxApp.jsx
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:

App.jsxApp.jsx
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:

App.jsxApp.jsx
<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

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.
RunePowered by Rune AI

Frequently Asked Questions

Why does the counter use useState?

useState stores a value that changes over time and tells React to re-render the component when the value changes. A plain variable would reset on every render.

What does the count + 1 inside the click handler do?

It computes the next count from the current value and passes it to setCount. React then re-renders the button with the new number.

Why use a functional update like setCount(previous => previous + 1)?

The functional form reads the latest value, which matters when several updates are queued or the next value depends on the previous one. For a simple click counter it is a safe habit.

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.