TL;DR: React is a JavaScript library for building component-based user interfaces. AI defaults to it because it dominates frontend training data. You need to understand components, props, state, JSX, and hooks to read React code confidently — and know when a simple HTML page would serve you better.
Why AI Coders Need to Know This
React is the most popular JavaScript UI library on the planet. According to the 2025 Stack Overflow Developer Survey, 39.5% of all developers use React — more than any other web framework. That dominance means one thing for vibe coders: AI will default to React constantly.
Ask Claude, Cursor, or Windsurf to build almost anything with a UI and you will get React back. A task list? React. A dashboard? React. A simple form that emails you? React. The model is not being thoughtful — it is pattern-matching against a training dataset where React is everywhere.
This creates a real problem for builders who do not understand React: you end up accepting code you cannot read, debug, or modify. When something breaks — and it will break — you are stuck. You cannot prompt your way out of a bug you do not understand.
The good news is that React's core mental model is not complicated. Once you understand the five foundational concepts (components, JSX, props, state, and hooks), you can read AI-generated React code, debug it with confidence, and — critically — recognize the many situations where React is the wrong tool entirely.
Real Scenario
You open Claude Code and type:
Prompt I Would Type
Build me a task manager app. I want to be able to:
- Add new tasks with a title and priority (low/medium/high)
- Mark tasks as complete
- Filter the list by status (all / active / completed)
- Delete tasks
Use React with hooks. Keep it in a single file for now. Add comments explaining what each part does.
This is a reasonable prompt for a learning exercise. You are asking for a real interactive app — one that needs to track and update data in response to user actions. That is exactly what React is designed for.
Within seconds, Claude returns a complete App.jsx file. It works. But if you have never seen React before, it looks like alphabet soup mixed with HTML. Let's decode it.
What AI Generated
Here is a realistic representation of the React component Claude would produce. Read the inline comments — they are your learning path through the code.
// React and hooks are imported from the react package
import { useState } from 'react';
// TaskItem is a child component — it renders a single task row
// It receives data (task) and callbacks (onToggle, onDelete) from its parent via props
function TaskItem({ task, onToggle, onDelete }) {
return (
// JSX looks like HTML but it's actually JavaScript
// className replaces class because "class" is a reserved JS word
<div className={`task-item ${task.completed ? 'task-item--done' : ''}`}>
<input
type="checkbox"
checked={task.completed}
onChange={() => onToggle(task.id)} // call parent function when checked
/>
<span className={`task-title priority-${task.priority}`}>
{task.title} {/* curly braces render JavaScript expressions in JSX */}
</span>
<span className="priority-badge">{task.priority}</span>
<button onClick={() => onDelete(task.id)}>Delete</button>
</div>
);
}
// App is the root component — it owns all the state and logic
function App() {
// useState returns [currentValue, setterFunction]
// tasks is an array; setTasks replaces it with a new array
const [tasks, setTasks] = useState([]);
// title and priority are separate pieces of form state
const [title, setTitle] = useState('');
const [priority, setPriority] = useState('medium');
// filter controls which tasks the list shows
const [filter, setFilter] = useState('all');
// addTask creates a new task object and appends it to the tasks array
function addTask(e) {
e.preventDefault(); // stop the form from reloading the page
if (!title.trim()) return; // don't add empty tasks
const newTask = {
id: Date.now(), // simple unique id using timestamp
title: title.trim(),
priority,
completed: false,
};
setTasks([...tasks, newTask]); // spread existing tasks, add the new one
setTitle(''); // clear the input field
}
// toggleTask flips a task's completed status
function toggleTask(id) {
setTasks(tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
}
// deleteTask removes a task by filtering it out
function deleteTask(id) {
setTasks(tasks.filter(task => task.id !== id));
}
// filteredTasks is derived from state — recalculates every render
const filteredTasks = tasks.filter(task => {
if (filter === 'active') return !task.completed;
if (filter === 'completed') return task.completed;
return true; // 'all'
});
// The return value is JSX — React converts this to real DOM elements
return (
<div className="app">
<h1>Task Manager</h1>
{/* Form to add a new task */}
<form onSubmit={addTask}>
<input
value={title} // controlled input — value tied to state
onChange={e => setTitle(e.target.value)} // update state on every keystroke
placeholder="Task title..."
/>
<select value={priority} onChange={e => setPriority(e.target.value)}>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
<button type="submit">Add Task</button>
</form>
{/* Filter buttons */}
<div className="filters">
{['all', 'active', 'completed'].map(f => (
<button
key={f}
className={filter === f ? 'active' : ''}
onClick={() => setFilter(f)}
>
{f}
</button>
))}
</div>
{/* Render one TaskItem per filtered task */}
<div className="task-list">
{filteredTasks.map(task => (
<TaskItem
key={task.id} // key helps React track list items efficiently
task={task} // pass task data as a prop
onToggle={toggleTask} // pass callback functions as props
onDelete={deleteTask}
/>
))}
</div>
<p>{tasks.filter(t => !t.completed).length} task(s) remaining</p>
</div>
);
}
export default App; // export so other files can import this component
That is about 80 lines of React. A beginner scanning it for the first time will see familiar words (function, return, button) mixed with unfamiliar syntax (useState, className, {task.title}). Let's decode the big ideas.
Understanding Each Part
Components: The Building Blocks
Everything in React is a component. A component is a JavaScript function that returns JSX — a description of what the UI should look like. In the code above, TaskItem and App are both components.
Components are like custom HTML elements you define yourself. Instead of writing the same task row markup 10 times, you define it once as TaskItem and reuse it. This is the core value of React: you build a system of composable, reusable pieces.
- Parent components render child components and pass data to them.
- Child components receive that data and render UI based on it.
- Data flows down — parent to child. Never directly up.
JSX: HTML-Like Syntax in JavaScript
JSX is the weird-looking HTML inside the JavaScript functions. It is not real HTML — it is a syntax extension that Babel (a build tool) transforms into React.createElement() calls before the browser sees it.
Key JSX rules to memorize:
- Use
classNameinstead ofclass(becauseclassis a reserved JavaScript keyword). - Wrap JavaScript expressions in
{curly braces}. - Self-closing tags must close:
<input />not<input>. - A component must return a single root element (or a
<Fragment>). - Comments use
{/* like this */}inside JSX.
Props: Data Flowing Into a Component
Props are how a parent component sends data to a child component. In the example, App passes task, onToggle, and onDelete to each TaskItem. Inside TaskItem, those values arrive as a single props object — the curly brace destructuring { task, onToggle, onDelete } just unpacks them.
Props are read-only inside the component that receives them. A child component cannot modify its own props. If data needs to change, the parent handles the change and passes the new value down.
State: Data That Changes Over Time
State is data that a component owns and can update. The useState hook creates a piece of state and returns two things: the current value and a setter function.
const [tasks, setTasks] = useState([]); // initial value is an empty array
When you call setTasks(newArray), React:
- Updates the stored state value.
- Re-renders the component (and its children) with the new value.
This is why the task list updates instantly when you add or delete a task. You are not manually manipulating the DOM — you are updating state, and React figures out what changed and updates only the necessary parts.
The key mental model: props come from outside, state lives inside.
Hooks: Adding Capabilities to Functions
Hooks are special React functions that let functional components do more. The most important ones:
useState— store and update data inside a component.useEffect— run code in response to changes (fetching data, setting timers, syncing with external systems).useContext— access shared data without passing props through every layer.useRef— hold a mutable value that does not trigger a re-render when changed.
Hooks always start with use and must be called at the top level of a component function — not inside loops, conditions, or nested functions. This is a rule React enforces strictly.
The Virtual DOM: Why React Re-renders Efficiently
When state changes, React does not rebuild the entire page. It maintains a lightweight in-memory representation of the DOM (the "virtual DOM"), compares it to what the real DOM looks like, and makes only the minimum required changes. This diffing process is called reconciliation and is what makes React fast for UIs with lots of changing data.
What AI Gets Wrong About React
AI-generated React has a reliable set of mistakes. Knowing them lets you catch issues before they become bugs.
Missing the key prop in lists
When rendering an array of elements, React needs a stable key prop on each item to track changes efficiently. AI sometimes omits it, or worse, uses the array index as a key.
// Bad — key is array index, causes bugs when items are reordered
{tasks.map((task, index) => <TaskItem key={index} task={task} />)}
// Good — key is a stable unique ID
{tasks.map(task => <TaskItem key={task.id} task={task} />)}
Using index as a key looks harmless but causes subtle, hard-to-diagnose bugs when the list order changes.
Mutating state directly
State in React must be treated as immutable. You cannot push to an array or assign a property directly — you must replace the whole value with a new one.
// Bad — mutates state directly, React may not re-render
tasks.push(newTask);
setTasks(tasks);
// Good — creates a new array, React sees the change
setTasks([...tasks, newTask]);
useEffect with a missing dependency array
The useEffect hook runs code as a side effect. The second argument — the dependency array — controls when it runs. Missing it means the effect runs on every render, which can cause infinite loops or unexpected API call floods.
// Dangerous — runs after every render
useEffect(() => {
fetchData();
});
// Correct — runs once after mount
useEffect(() => {
fetchData();
}, []); // empty array = run once
// Correct — runs when userId changes
useEffect(() => {
fetchUserData(userId);
}, [userId]);
Reaching for React when plain JavaScript is enough
This is the biggest AI mistake. A simple contact form, a marketing landing page, or a static documentation site does not need React. AI uses React by default, not by judgment. See the section below on when to say no.
Putting everything in one giant component
AI will often generate a 300-line single component instead of splitting logic into smaller, focused pieces. This makes the code harder to read and impossible to test. Ask the AI to refactor by responsibility: "Split this into separate components — one for the form, one for the list, one for each item."
Quick Review Checklist
After receiving AI-generated React: check every list for stable key props, verify state updates return new objects/arrays, review every useEffect for a dependency array, and ask yourself: does this actually need React?
When NOT to Use React
This is the section AI will never tell you about, because AI does not optimize for your project — it optimizes for pattern completion.
React is the right tool when:
- Your UI has complex, dynamic state (task lists, form wizards, dashboards, real-time data).
- You need lots of components that share data.
- You are building an app users interact with heavily, not just read.
- You need a rich ecosystem of libraries and community support.
React is the wrong tool when:
- You are building a marketing page, blog, or portfolio with minimal interactivity.
- Speed and simplicity matter more than component architecture.
- You are deploying a static site with no backend.
- Your team has no JavaScript experience and the deadline is tight.
- You want a page that loads in under a second on a slow connection.
React adds real overhead: a build tool (Vite or CRA), a node_modules folder that can exceed 100MB, JSX compilation, and a JavaScript bundle that must load and execute before the user sees anything. For a simple brochure site, that is all downside.
The right counter-prompt when AI defaults to React unnecessarily: "This is a simple static page with one contact form. Rewrite this as plain HTML, CSS, and vanilla JavaScript — no React, no build step." AI can do it. You just have to ask.
How to Debug React With AI
React errors can feel cryptic, but they are usually traceable. Here is how to work through them with AI tools.
Use React DevTools
Install the React DevTools browser extension. It adds a Components panel to Chrome DevTools where you can inspect the component tree, see the current props and state of any component, and watch state change in real time as you interact with the app. This is your primary debugging tool for React — without it, you are guessing.
Read the error message first
React's error messages are actually good. "Each child in a list should have a unique key prop" tells you exactly what the problem is. "Cannot read properties of undefined" tells you a variable was not what you expected. Copy the full error message and paste it into your AI tool with the relevant component code for fast, targeted fixes.
Cursor tips
- Use Cursor's inline chat to ask about a specific function: highlight
toggleTaskand ask "explain what this does and whether it's mutating state correctly." - Ask Cursor to add
console.logstatements at strategic points, then read the output before asking it to fix anything. - When a component is re-rendering too often, ask: "Add a console.log at the top of this component so I can see how many times it renders."
Windsurf tips
- Windsurf's Cascade feature understands multi-file context well. When a prop is passed through 3 layers of components, ask Cascade to trace the full data flow.
- For state bugs, describe the expected behavior vs. the actual behavior in plain English. Windsurf translates that into targeted code fixes better than vague "it's broken" prompts.
Claude Code tips
- Paste the component plus the exact error message. Claude Code is strong at explaining what went wrong and why — not just patching it.
- Ask: "What is the minimal reproduction of this bug?" Claude Code will often strip the code down to the core issue, which helps you understand it rather than just accepting a fix.
- Use
CLAUDE.mdin your project root to specify "this project uses React 19 with hooks only — no class components." See our Claude Code Beginners Guide for how to set this up.
What to Learn Next
React builds on JavaScript fundamentals. If any part of the code above felt fuzzy, shore up these foundations first:
- What Is JavaScript? — the language React is built on.
- What Is a Function? — React components are just functions.
- What Is Async/Await? — essential for fetching data inside React.
- What Is the DOM? — understand what React is abstracting.
- What Is an API? — most React apps fetch data from one.
- What Is a Component? — components are React's building blocks. Understand them deeply.
- What Is Vite? — the modern build tool that replaced Create React App. AI always uses it now.
Next Step
Build the task manager above. Break the addTask function intentionally and debug it with React DevTools. Nothing teaches React faster than watching state change in real time.
FAQ
React is a JavaScript library for building user interfaces. It lets you break a web app into small, reusable components — like building with LEGO bricks. Each component manages its own content and can update independently when data changes.
AI models are trained on massive amounts of code from GitHub, Stack Overflow, and developer blogs. React is by far the most common frontend library in that training data. When you ask for a "task manager app" or a "dashboard," the AI defaults to React because that's what most of its training examples look like.
Props are data passed into a component from its parent — they are read-only inside the component. State is data that a component manages itself and can change over time. Think of props as arguments you pass to a function, and state as variables the function manages internally.
Yes. React is a JavaScript library, not a standalone language. You should be comfortable with JavaScript functions, arrays, objects, and basic async concepts before React will make sense. AI-generated React code will be confusing without that foundation.
Skip React when building a simple marketing site, blog, or content page with minimal interactivity. Plain HTML and CSS will load faster, cost less, and be easier to maintain. React adds real complexity — components, hooks, build tools, and a node_modules folder — that is only worth it when you genuinely need dynamic, stateful UI.