This react tutorial for beginners step by step will take you from zero to a working app in under an hour. You will learn what React is, set up a modern project with Vite, and build a real task tracker while learning components, JSX, props, state, and events along the way.
No prior React knowledge is assumed — just basic JavaScript (variables, functions, arrays). Every code block is complete and copy-pasteable. Let’s build.
What Is React and Why Does It Matter?
React is a JavaScript library created by Meta for building user interfaces. Instead of manually updating the page with document.getElementById every time data changes (like you did in vanilla JavaScript projects), React lets you describe what the page should look like and it handles updating the actual page for you.
The core idea is simple: your UI is a function of your state. When state changes, React re-renders. This is the same “change the data, re-render” pattern from plain JavaScript — React just automates the tedious parts.
Why learn React in 2026? It remains the most in-demand front-end skill by a wide margin. The majority of front-end job postings ask for React, the ecosystem (Next.js, React Native, thousands of libraries) is unmatched, and once you understand React’s component model, learning any other framework becomes dramatically easier. If you want the full context of where React fits in your journey, check our guide on how to learn coding from scratch.
Prerequisites: Node.js and npm
Before we start this react tutorial for beginners step by step, you need Node.js installed — it gives you npm, the package manager we use to create and run the project.
- Go to nodejs.org and download the LTS (Long Term Support) version for your operating system.
- Run the installer with default settings.
- Open a terminal and verify the installation:
node --version npm --versionYou should see version numbers like
v22.x.xand10.x.x. If you see them, you are ready.
Step 1: Create Your First React App with Vite
We will use Vite (pronounced “veet”) — the modern, lightning-fast build tool that the React team now recommends over the old Create React App. Open your terminal, navigate to where you keep projects, and run:
npm create vite@latest my-task-tracker -- --template react
The terminal will ask a couple of setup questions — accept the defaults. Then:
cd my-task-tracker
npm install
npm run dev
Your terminal will show a local URL, usually http://localhost:5173. Open it in your browser and you will see the default Vite + React starter page. Congratulations — you have a working React app! The dev server watches your files and instantly refreshes the browser whenever you save, which makes learning incredibly fast.
Take a look at the project structure. The files you care about right now are:
src/main.jsx— the entry point that renders your app into the pagesrc/App.jsx— your main component (this is where we will work)index.html— the HTML shell with a<div id="root">where React mounts
For the official details on everything Vite can do, the Vite guide is excellent.
Step 2: Your First Component and JSX
A component is a reusable piece of UI — a function that returns what should appear on screen. Open src/App.jsx, delete everything in it, and write:
function App() {
return (
<div>
<h1>My Task Tracker</h1>
<p>Built with my first React app!</p>
</div>
);
}
export default App;
Save the file and watch your browser update instantly. That HTML-looking syntax inside the JavaScript function is called JSX — JavaScript XML. It looks like HTML, but it is JavaScript: you can embed any JavaScript expression inside curly braces {}:
function App() {
const appName = "My Task Tracker";
const taskCount = 3;
return (
<div>
<h1>{appName}</h1>
<p>You have {taskCount} tasks. {taskCount > 0 ? "Keep going!" : "All done!"}</p>
</div>
);
}
export default App;
JSX rules to remember: return a single parent element (wrap siblings in a <div> or fragment <></>), use className instead of class, and close every tag including <br /> and <img />.
Step 3: Props — Passing Data to Components
Real apps are built from many small components. Props (short for properties) are how a parent component passes data down to a child component — like function arguments, but for UI. Let’s split our app into components:
// A reusable Task component that receives data via props
function Task({ title, done }) {
return (
<li style={{ textDecoration: done ? "line-through" : "none" }}>
{title}
</li>
);
}
function App() {
return (
<div>
<h1>My Task Tracker</h1>
<ul>
<Task title="Learn React components" done={true} />
<Task title="Understand props" done={true} />
<Task title="Master useState" done={false} />
</ul>
</div>
);
}
export default App;
Notice the destructuring { title, done } in the function signature — it pulls the props out of the props object directly. Passing done={true} with curly braces sends a real boolean; done="true" with quotes would send the string “true”, a classic beginner bug. Props flow one way: parent to child, never the reverse.
Step 4: State with useState — Making It Interactive
So far our app is static. State is data that changes over time, and useState is the hook that gives a component its own state. When state changes, React automatically re-renders the component. This is the single most important concept in React — learn react for beginners really means learning state.
import { useState } from "react";
function Counter() {
// count = current value, setCount = function to update it
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Three things to understand here:
useState(0)creates a state variable initialized to0and returns a pair: the value and its updater function.- Never modify state directly (
count++does nothing visible). Always call the setter — that is what triggers the re-render. onClick={() => setCount(count + 1)}passes a function to run on click. WritingonClick={setCount(count + 1)}without the arrow function would call it immediately during render — another classic beginner bug.
Step 5: Handling Events and Forms
React events look like HTML event attributes but are camelCase: onClick, onChange, onSubmit. Let’s build an input form — the pattern you will use in nearly every React app:
import { useState } from "react";
function TaskForm({ onAddTask }) {
const [text, setText] = useState("");
function handleSubmit(e) {
e.preventDefault(); // stop the page from reloading
if (text.trim() === "") return;
onAddTask(text);
setText(""); // clear the input
}
return (
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Enter a new task..."
/>
<button type="submit">Add Task</button>
</form>
);
}
This is a controlled component: React state is the single source of truth for the input’s value, and every keystroke updates state via onChange. The onAddTask prop is a callback — the child calls a function the parent provided, which is how data flows back up (since props only flow down).
Step 6: Build the Complete Task Tracker App
Now let’s assemble everything — components, props, state, and events — into a complete working app. Replace your entire src/App.jsx with this:
import { useState } from "react";
function TaskForm({ onAddTask }) {
const [text, setText] = useState("");
function handleSubmit(e) {
e.preventDefault();
if (text.trim() === "") return;
onAddTask(text);
setText("");
}
return (
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Enter a new task..."
/>
<button type="submit">Add Task</button>
</form>
);
}
function TaskItem({ task, onToggle, onDelete }) {
return (
<li>
<span
onClick={() => onToggle(task.id)}
style={{
textDecoration: task.done ? "line-through" : "none",
cursor: "pointer",
}}
>
{task.text}
</span>
<button onClick={() => onDelete(task.id)}>Delete</button>
</li>
);
}
function App() {
const [tasks, setTasks] = useState([
{ id: 1, text: "Learn React components", done: true },
{ id: 2, text: "Build a task tracker", done: false },
]);
function addTask(text) {
const newTask = { id: Date.now(), text: text, done: false };
setTasks([...tasks, newTask]); // create a NEW array — never push into state
}
function toggleTask(id) {
setTasks(
tasks.map((task) =>
task.id === id ? { ...task, done: !task.done } : task
)
);
}
function deleteTask(id) {
setTasks(tasks.filter((task) => task.id !== id));
}
const remaining = tasks.filter((task) => !task.done).length;
return (
<div>
<h1>My Task Tracker</h1>
<p>
{remaining} of {tasks.length} tasks remaining
</p>
<TaskForm onAddTask={addTask} />
<ul>
{tasks.map((task) => (
<TaskItem
key={task.id}
task={task}
onToggle={toggleTask}
onDelete={deleteTask}
/>
))}
</ul>
</div>
);
}
export default App;
Save, and try it: add tasks, click a task to toggle it done, delete tasks. You just built a real React app. Study these patterns — they appear in every React codebase on earth:
- State lives in the parent (
Appowns the tasks array) and flows down via props. - State updates are immutable:
[...tasks, newTask],map, andfiltercreate new arrays instead of mutating the old one. React detects the new array reference and re-renders. keyon list items helps React track which item is which when the list changes. Use a stable ID, never the array index, when items can be added or removed.- Derived values like
remainingare computed during render from state — do not store them as separate state.
Common Beginner Mistakes (and Fixes)
Every beginner hits these — now you will recognize them instantly:
- Mutating state directly:
tasks.push(newTask)thensetTasks(tasks)will not re-render reliably. Always create a new array or object. - Calling the handler instead of passing it:
onClick={handleClick()}runs during render. You wantonClick={handleClick}oronClick={() => handleClick(id)}. - Forgetting
e.preventDefault()in form submits, causing a full page reload that wipes your state. - Missing
keyprops in lists, causing weird UI glitches when items reorder. - Stale state in rapid updates: when the new state depends on the old state, use the updater form:
setCount(prev => prev + 1).
What’s Next
You have completed this react tutorial for beginners step by step: you set up a Vite project, built components, used JSX and props, managed state with useState, handled events, and shipped a working task tracker. That is genuinely the core of React — everything else (routing, data fetching, context) builds on these ideas.
Where to go from here:
- Sharpen your JavaScript fundamentals with our 10 JavaScript projects for beginners — stronger JS makes React dramatically easier.
- Learn what an API is in our what is an API guide, then try fetching real data into your task tracker with
useEffectandfetch. - Follow the full roadmap in how to learn coding from scratch to plan your next months of learning.
For official documentation, react.dev has the best modern React tutorial anywhere — work through their “Learn React” section next, and keep the Vite guide bookmarked for build and deployment questions. Build first react app is done — now build your second one.