TL;DR: A component is a reusable, self-contained piece of UI. Every React, Vue, and Svelte app is built from components. Props pass data in. Children let you nest components inside each other. Composition lets you build complex UIs from simple pieces. AI generates components by default — understanding them means understanding your own code.

Why AI Coders Need to Know This

If you have used Cursor, Claude Code, or Windsurf to build anything with a UI, you have received components back. Not one or two — dozens. A simple dashboard prompt can generate 12 or more separate files, each containing a component. If you do not understand what a component is, you are accepting code you cannot read, cannot debug, and cannot modify with confidence.

Components are not a React-specific idea. They are the foundational mental model behind React, Vue, Svelte, Angular, and Web Components — the five dominant UI paradigms in modern web development. Understanding this one concept unlocks all of them.

More importantly: AI generates component-based code automatically. The model is not making a deliberate architectural decision — it is pattern-matching against a training corpus where components are everywhere. That means you need to understand the output. When something breaks, "it doesn't work" is not enough context to fix it. You need to know which component is broken, what data it is receiving, and where that data is coming from.

This article gives you that foundation. No theory. No computer science textbook definitions. Just what components do, why they exist, and how to read the ones AI hands you.

The Construction Metaphor: Prefab Sections

If you have worked in construction, you already understand components. You just have not called them that.

Consider a prefab wall section — a framed, insulated, pre-drywalled panel that rolls off a factory floor ready to install. You do not rebuild that wall from lumber and nails every time you need it. You manufacture it once, define its specifications (height, width, insulation rating, window cutout: yes/no), and then replicate it across every floor of every building that needs it. Swap out the window spec and you get a different wall. The manufacturing process is identical.

Components work exactly the same way:

  • The component definition is the blueprint — how the wall section is built.
  • Props are the customizable specs — window or no window, 8-foot or 9-foot ceiling.
  • Instances are the physical panels that get installed — each one is its own copy, with its own measurements passed in.
  • Composition is how you assemble those panels into a full building.

When Cursor generates a Card component, it is giving you the blueprint for a prefab panel. When it renders twelve cards on a dashboard, each one is an independent instance of that panel, customized with different data. You change the blueprint once; every instance updates.

That is why components exist. Not because developers love abstraction — because repeating the same markup 50 times and trying to maintain it is how projects become unmaintainable. One blueprint, many installations.

Real Scenario: You Ask for a Dashboard, You Get 12 Files

You open Cursor and type:

Prompt I Would Type

Build me an admin dashboard for a SaaS app. I need:
- A header with the app logo and user avatar
- A left sidebar with navigation links
- A main content area with 4 metric cards (revenue, users, churn, MRR)
- A recent activity feed
- A data table showing the last 10 orders

Use React and Tailwind. Break it into components.

Cursor does not hand you one file. It hands you a folder structure that looks something like this:

src/
  components/
    Header.jsx
    Sidebar.jsx
    SidebarLink.jsx
    MetricCard.jsx
    MetricGrid.jsx
    ActivityFeed.jsx
    ActivityItem.jsx
    OrdersTable.jsx
    OrderRow.jsx
    Avatar.jsx
    Badge.jsx
  pages/
    Dashboard.jsx
  App.jsx

Twelve files. Each one is a component. That might feel overwhelming if you were expecting one file. But look at the names and a pattern emerges: there is a component for the whole thing, components for each major section, and components for the repeating pieces inside each section.

This is component hierarchy — the tree of parent and child components that makes up your entire UI. App renders Dashboard. Dashboard renders Header, Sidebar, MetricGrid, ActivityFeed, and OrdersTable. MetricGrid renders four MetricCard instances, each with different numbers. OrdersTable renders one OrderRow per order.

Every UI you will ever build follows this same tree structure. Once you see it, you cannot unsee it.

What AI Generated: A Multi-Component React App

Here is a realistic representation of the core components from that dashboard. Read the comments — they decode what each part does.

// MetricCard.jsx
// A single metric card — title, value, and a trend indicator
// It accepts data through props and renders the same layout every time

function MetricCard({ title, value, trend, trendLabel }) {
  // Props are destructured from the argument object
  // title = "Monthly Revenue", value = "$42,800", trend = "up", trendLabel = "+12%"

  return (
    <div className="metric-card">
      <p className="metric-card__title">{title}</p>
      <p className="metric-card__value">{value}</p>
      <span className={`trend trend--${trend}`}>
        {trendLabel}
      </span>
    </div>
  );
}

export default MetricCard;
// MetricGrid.jsx
// Renders four MetricCard components in a grid
// The data lives here — MetricCard just displays whatever it receives

import MetricCard from './MetricCard';

const metrics = [
  { title: 'Monthly Revenue', value: '$42,800', trend: 'up',   trendLabel: '+12%' },
  { title: 'Active Users',    value: '1,284',   trend: 'up',   trendLabel: '+8%'  },
  { title: 'Churn Rate',      value: '2.4%',    trend: 'down', trendLabel: '-0.3%'},
  { title: 'MRR',             value: '$38,200',  trend: 'up',   trendLabel: '+9%'  },
];

function MetricGrid() {
  return (
    <div className="metric-grid">
      {metrics.map((metric) => (
        // Each MetricCard gets its own copy of the data via props
        // key helps React track each card efficiently in the list
        <MetricCard
          key={metric.title}
          title={metric.title}
          value={metric.value}
          trend={metric.trend}
          trendLabel={metric.trendLabel}
        />
      ))}
    </div>
  );
}

export default MetricGrid;
// Dashboard.jsx
// The page-level component — assembles all the sections together
// It does not contain any UI details itself — it just composes other components

import Header from '../components/Header';
import Sidebar from '../components/Sidebar';
import MetricGrid from '../components/MetricGrid';
import ActivityFeed from '../components/ActivityFeed';
import OrdersTable from '../components/OrdersTable';

function Dashboard() {
  return (
    <div className="dashboard-layout">
      <Header />
      <div className="dashboard-layout__body">
        <Sidebar />
        <main className="dashboard-layout__main">
          <MetricGrid />
          <ActivityFeed />
          <OrdersTable />
        </main>
      </div>
    </div>
  );
}

export default Dashboard;

Notice how Dashboard does not know or care what a metric card looks like. It just says "put MetricGrid here." MetricGrid does not know what the data will be used for — it just passes it down to MetricCard. Each component has one job.

Understanding Each Part

What a Component Actually Is

In React, a component is a JavaScript function whose name starts with a capital letter and returns JSX — a description of what the UI should look like. That is the entire technical definition.

// This is a valid React component
function Greeting() {
  return <h1>Hello, world!</h1>;
}

// So is this
const Button = () => <button>Click me</button>;

When React sees <Greeting /> in JSX, it calls the Greeting function and inserts whatever it returns into the DOM. Components can be nested inside other components, passed around like values, and rendered conditionally based on state. They are just functions — which means everything you know about functions applies to them.

Props: Data Flowing Into a Component

Props (short for properties) are how a parent component passes data to a child component. They arrive as an object argument and you can destructure them directly in the function signature.

// Parent passes data as JSX attributes
<MetricCard title="Revenue" value="$42,800" trend="up" trendLabel="+12%" />

// Child receives those attributes as a props object
function MetricCard({ title, value, trend, trendLabel }) {
  // title = "Revenue", value = "$42,800", etc.
  return (
    <div>
      <p>{title}</p>
      <p>{value}</p>
    </div>
  );
}

Props are read-only. A component cannot modify its own props. If data needs to change, the parent holds the state and passes down a new value. This one-way data flow is what makes React apps predictable — you always know where data comes from.

Props can be anything: strings, numbers, booleans, arrays, objects, and even functions. When you pass a function as a prop (like onDelete or onClick), the child can call it to signal something back to the parent without directly touching parent state. This is the React pattern for child-to-parent communication.

Children: Nesting Content Inside a Component

There is a special prop called children that lets you nest content between a component's opening and closing tags — just like you nest content between HTML tags.

// A Card wrapper component that accepts children
function Card({ children, className }) {
  return (
    <div className={`card ${className || ''}`}>
      {children}  {/* whatever gets nested inside <Card>...</Card> appears here */}
    </div>
  );
}

// Usage — you can put anything inside
<Card className="card--featured">
  <h2>Monthly Revenue</h2>
  <p className="big-number">$42,800</p>
</Card>

The Card component does not know or care what gets put inside it. It just provides the wrapper styling. This is a powerful pattern for layout components — Modal, Sidebar, Drawer, Tooltip — anything that wraps other content.

Composition: Building Complex UIs from Simple Pieces

Composition is the art of assembling components. The key insight is that components do not need to know about each other to work together. The parent knows about both children, but the children only know about their own props.

This is why the dashboard example works: MetricCard has no idea it is inside a MetricGrid which is inside a Dashboard. It just renders whatever props it receives. That isolation is what makes components reusable — you can drop MetricCard into any page in any app and it will work, as long as you pass the right props.

Think of it like standardized building materials. A 2x4 stud does not know if it is in a wall, a floor joist, or a roof truss. It just has standard dimensions. The builder composes those standard pieces into structures. You do the same with components.

Components Across Frameworks: Same Idea, Different Syntax

The component mental model is universal. The syntax varies by framework. Here is the same simple Button component written in four different ways.

React (JSX in a .jsx or .tsx file)

// Button.jsx
function Button({ label, onClick, variant = 'primary' }) {
  return (
    <button
      className={`btn btn--${variant}`}
      onClick={onClick}
    >
      {label}
    </button>
  );
}

export default Button;

// Usage
<Button label="Save Changes" onClick={handleSave} variant="primary" />

Vue 3 (Single File Component in a .vue file)

<!-- Button.vue -->
<template>
  <button :class="`btn btn--${variant}`" @click="$emit('click')">
    {{ label }}
  </button>
</template>

<script setup>
// defineProps declares what data this component accepts
const props = defineProps({
  label: String,
  variant: { type: String, default: 'primary' }
});
// defineEmits declares what events it can fire upward
defineEmits(['click']);
</script>

<!-- Usage -->
<Button label="Save Changes" variant="primary" @click="handleSave" />

Svelte 5 (Single File Component in a .svelte file)

<!-- Button.svelte -->
<script>
  // $props() is Svelte 5's rune for declaring component props
  let { label, variant = 'primary', onclick } = $props();
</script>

<button class="btn btn--{variant}" {onclick}>
  {label}
</button>

<!-- Usage -->
<Button label="Save Changes" variant="primary" {onclick} />

Vanilla Web Components (built into the browser, no framework needed)

// my-button.js
class MyButton extends HTMLElement {
  connectedCallback() {
    const label   = this.getAttribute('label') || 'Button';
    const variant = this.getAttribute('variant') || 'primary';
    this.innerHTML = `<button class="btn btn--${variant}">${label}</button>`;
  }
}

// Register the custom element — now <my-button> works in any HTML file
customElements.define('my-button', MyButton);

// Usage in HTML
<my-button label="Save Changes" variant="primary"></my-button>

The syntax is different in each case. But the concept is identical: a named, reusable UI unit that accepts inputs (props/attributes) and renders output (HTML). Once you have this mental model, switching between frameworks is a syntax translation problem — not a conceptual one. Read about the JavaScript fundamentals that underpin all of them.

What AI Gets Wrong About Components

AI generates components constantly — but it makes reliable mistakes. Knowing the patterns saves you debugging time.

Too many micro-components

AI sometimes splits UI into absurdly small components that will never be reused. A PageTitle component that renders one <h1> tag, a Divider that renders one <hr>, a Spacer that adds margin. These add file count without adding value. You do not need a component for every HTML element — only for things you actually reuse or that have meaningful logic.

One giant component that does everything

The opposite problem: a 400-line component that handles fetching, filtering, rendering, and user interactions all in one function. This is AI taking the path of least resistance. Ask it to refactor: "Split this component by responsibility — separate the data fetching from the display logic, and extract the repeating list item into its own component."

Prop drilling

Prop drilling is when data gets passed through 3, 4, or 5 layers of components, even though only the bottom layer actually uses it. Every intermediate component just relays the prop along. It looks like this:

// App passes userId to Dashboard
<Dashboard userId={userId} />

// Dashboard passes it to Sidebar (even though Sidebar doesn't use it)
<Sidebar userId={userId} />

// Sidebar passes it to UserMenu (still not using it)
<UserMenu userId={userId} />

// UserMenu finally uses it — three levels down
function UserMenu({ userId }) {
  return <a href={`/profile/${userId}`}>My Profile</a>;
}

This makes components brittle — adding a new layer means threading a prop through every level. The fix is useContext (React), Provide/Inject (Vue), or a state management solution like Zustand. Understanding state management is the natural next step after mastering components.

Not extracting reusable pieces

AI will often write the same button styling or card layout three times in three different components instead of extracting a shared Button or Card component. This is the construction equivalent of framing the same wall from scratch three times instead of using a prefab. When the design changes, you update three places instead of one. Ask the AI: "Are there any UI patterns in this code that appear more than once? Extract them into shared components."

Missing the key prop in lists

Whenever AI renders an array of components, it needs a stable key prop on each one. AI occasionally uses the array index as the key, which looks fine until items get reordered or deleted.

// Bad — index as key causes subtle bugs with reordering
{items.map((item, index) => <Card key={index} item={item} />)}

// Good — use a stable unique ID from the data
{items.map((item) => <Card key={item.id} item={item} />)}

Quick Review Checklist

After receiving AI-generated components: check every list for stable key props, look for prop drilling that should be refactored, identify components that appear only once and may not need to be extracted, and look for large single-component files that need to be split.

How to Debug Component Issues

Most component bugs fall into a small set of categories: wrong data being passed, data not being updated, or a component not rendering when expected. Here is how to trace them.

Use React DevTools

Install the React DevTools browser extension. It adds a Components panel to Chrome or Firefox DevTools where you can inspect the entire component tree, click any component, and see exactly what props and state it currently holds. This is the most important debugging tool in your React toolkit. When a component is not rendering correctly, open DevTools and check what it actually received — nine times out of ten, the props are not what you thought.

Trace data flow from the top

When a component shows wrong data, start at the top of the tree and work down. Where does this data originate? Which component holds it in state? How many layers does it pass through before reaching the broken component? This traces back to understanding how the DOM and data relate.

// Add a temporary console.log inside the component to see what arrived
function MetricCard({ title, value, trend, trendLabel }) {
  console.log('MetricCard props:', { title, value, trend, trendLabel });
  // ... rest of component
}

Add the log, check the browser console, verify the props match what you expect. If the props are wrong, the bug is in the parent — not in this component.

Check prop types at the boundaries

A common source of bugs is passing the wrong type. A component expects a number but receives a string. It expects an array but receives undefined (because the data has not loaded yet). In React, you will often see errors like "Cannot read properties of undefined" when a component tries to render before its data exists.

// Safe pattern: provide a default value to handle the loading case
function OrdersTable({ orders = [] }) {
  // If orders is undefined, default to empty array — no crash
  return (
    <table>
      {orders.map(order => <OrderRow key={order.id} order={order} />)}
    </table>
  );
}

Ask AI to explain a specific component

If a generated component makes no sense, paste it into Claude or Cursor with a direct question: "What does this component do? What props does it expect? What happens if orders is undefined?" AI is excellent at explaining the code it generates when asked directly. Use this as a learning tool, not just a fix tool.

What to Learn Next

Components do not exist in isolation. They are built on JavaScript, connected to the DOM, powered by state, and assembled by frameworks. These are the foundations you need next:

  • What Is React? — the framework that made components the default way to build for the web.
  • What Is a Function? — React components are functions; this is foundational.
  • What Is JavaScript? — the language components are written in.
  • What Is the DOM? — what components are ultimately modifying.
  • What Is State Management? — when your components need to share data beyond props.
  • What Is TypeScript? — add type safety to your component props so AI and your editor can catch mistakes before runtime.
  • What Is npm? — where you install component libraries like Material UI, shadcn/ui, and Radix that save you from building every component yourself.

Next Step

Take any component AI generates for you and trace its prop chain manually. Find where each prop comes from — which parent passes it, and where that data originally lives. You will understand your own code at a level no amount of vibe coding alone can give you.

FAQ

A component is a reusable chunk of UI — its own markup, logic, and sometimes styling bundled together as a single named unit. Think of it like a prefab wall section in construction: you design it once, then snap it into place wherever you need it. In React, every component is a JavaScript function that returns what the UI should look like.

Props are data passed into a component from outside — they are read-only inside the component, like instructions handed down from a supervisor. State is data the component manages itself and can change over time — like a worker's personal notepad. Props configure a component; state makes it interactive.

AI tools are trained on millions of GitHub repositories, Stack Overflow answers, and developer tutorials. The vast majority of modern frontend code is written with React, Vue, or Svelte — all of which are component-based. So when you ask for a dashboard or a form, the AI defaults to components because that is what almost all of its training examples look like.

The concept is identical — a named, reusable UI unit with inputs (props) and optionally its own state. The syntax is different. React uses JSX inside JavaScript functions. Vue uses Single File Components with a template, script, and style section. Svelte uses a similar single-file format but compiles away the framework at build time. Once you understand the component mental model in one framework, the others make intuitive sense.

There is no magic number. A common rule of thumb: if a piece of UI is reused in more than one place, extract it into a component. If a single component file grows past about 200–300 lines, it is probably doing too much and should be split. AI tends to err in both directions — sometimes generating hundreds of tiny one-use components, sometimes dumping everything into a single giant component. Use your judgment and ask the AI to refactor when things feel unwieldy.