React Components
A component is the fundamental building block of a React application — a reusable, self-contained piece of UI described in JavaScript. Instead of writing one giant HTML template, you write small functions that each return a piece of markup, then combine those functions like building blocks into a full page. Learning to think in components — how to define them, compose them, and split a UI into the right pieces — is the single most important skill in React, and everything else in this course (props, state, hooks) builds on top of it.
Overview / How it works
In modern React, a component is simply a JavaScript function whose name starts with a capital letter and which returns JSX — a syntax that looks like HTML but is actually JavaScript. When you write <h1>Hello</h1> inside a component, a build tool (Babel or the compiler built into your bundler) transforms that JSX into a call to React’s element-creation function, roughly jsx("h1", { children: "Hello" }). That call does not touch the DOM at all — it returns a plain JavaScript object called a React element, which is just a description of what should appear on screen: a type ("h1", or a component function like Greeting) and props. This lightweight, in-memory description is often called the virtual DOM.
Rendering an app means calling createRoot(...).render(<App />). React calls the App function, which returns more JSX — possibly JSX that references other components. React keeps calling each referenced component function, recursively, until it has resolved everything down to plain HTML tags (called host elements: div, h1, button, and so on). The result is a tree of elements that mirrors exactly how your JSX is nested. React then walks that tree and creates the real DOM nodes to match it in a single batch — this step is called the commit phase. Once your app updates (which you’ll learn about in the State lessons), React repeats the process, compares (“reconciles”) the new tree against the previous one, and updates only the real DOM nodes that actually changed — it does not tear down and rebuild the whole page.
This is why components matter: they are not just a way to avoid repeating markup, they are the unit React uses to organize, compare, and update your UI. Every capitalized JSX tag (<Profile />) is a call to a component function; every lowercase tag (<div>) is a real DOM element. A component should ideally be a pure function of its inputs — given the same props, it should always describe the same UI, with no side effects (like changing variables outside itself) during rendering. Side effects belong in event handlers or the useEffect hook, covered in later lessons.
Syntax
The general shape of a function component:
function ComponentName(props) {
// any plain JavaScript logic here
return (
// JSX describing the UI
);
}
export default ComponentName;
| Part | Explanation |
|---|---|
function ComponentName |
A regular JS function (or arrow function) whose name is PascalCase (capitalized). React uses the capital letter to distinguish your components from built-in HTML tags in JSX. |
props |
A single object argument holding any data passed in from a parent, e.g. <Profile name="Ada" /> passes { name: "Ada" }. Often destructured directly in the parameter list: function Profile({ name }). |
return ( ... ) |
Must return exactly one thing: a single JSX element (which can contain any number of nested children), an array, a string/number, or null to render nothing. |
| JSX | HTML-like syntax compiled to JavaScript. Use { } to embed any JavaScript expression, className instead of class, and self-close void tags like <img />. |
export default |
Makes the component importable from other files: import ComponentName from "./ComponentName";. |
Examples
Example 1: A minimal component
import { createRoot } from "react-dom/client";
function Greeting() {
return <h1>Hello, world!</h1>;
}
createRoot(document.getElementById("root")).render(<Greeting />);
Output:
Renders an h1 heading reading "Hello, world!" as the only content on the page.
Greeting is a function that returns one JSX element, <h1>. Calling createRoot(...).render(<Greeting />) mounts it into the DOM node with id root. Notice the tag is capitalized (<Greeting />) — that tells React “call this function,” not “create an HTML tag named greeting.”
Example 2: Composing multiple components
function Header() {
return (
<header>
<h1>My Site</h1>
</header>
);
}
function Footer() {
return (
<footer>
<p>© 2026 My Site</p>
</footer>
);
}
function Profile({ name, role }) {
return (
<div className="profile">
<h2>{name}</h2>
<p>{role}</p>
</div>
);
}
function App() {
return (
<>
<Header />
<Profile name="Ada Lovelace" role="Mathematician" />
<Footer />
</>
);
}
export default App;
Output:
Renders a page with a header reading "My Site", a profile section showing the heading "Ada Lovelace" and the text "Mathematician", and a footer reading "© 2026 My Site".
App doesn’t render any HTML directly — it composes three smaller components. Because a component can only return one root node, App wraps its three children in a Fragment (<>...</>), which groups elements without adding an extra DOM node. Profile demonstrates passing data via props: the parent supplies name and role as JSX attributes, and the child reads them from its destructured parameter.
Example 3: Rendering a list of components
const members = [
{ id: 1, name: "Ada Lovelace", role: "Mathematician" },
{ id: 2, name: "Alan Turing", role: "Computer Scientist" },
{ id: 3, name: "Grace Hopper", role: "Programmer" },
];
function TeamMember({ name, role }) {
return (
<li>
<strong>{name}</strong> — {role}
</li>
);
}
function TeamList() {
return (
<ul>
{members.map((member) => (
<TeamMember key={member.id} name={member.name} role={member.role} />
))}
</ul>
);
}
export default TeamList;
Output:
Renders an unordered list with three items: "Ada Lovelace — Mathematician", "Alan Turing — Computer Scientist", and "Grace Hopper — Programmer".
This is the realistic pattern: instead of hand-writing one TeamMember per person, TeamList maps over an array of data and produces one TeamMember component per entry, passing each item’s fields as props. The key prop (set to each member’s unique id) is required whenever you render a list of elements from an array — it’s how React tracks which element is which across renders, explained further below.
How it works step by step (Under the hood)
When createRoot(...).render(<App />) runs for the first time, React performs, in order:
- Render: React calls
App(). WhereverAppreturns JSX referencing another component (like<Profile />), React calls that function too, and keeps recursing until every branch of the tree resolves to plain host elements (div,h1,li, etc). The result is a tree of React elements — the virtual DOM. - Reconcile: On the very first render there’s nothing to compare against, so React simply walks the whole tree. On any later render (triggered by state or prop changes, covered in the State and Hooks lessons), React builds a new tree and diffs it against the previous one, element by element, to figure out the minimal set of changes.
- Commit: React takes the (initial or diffed) result and applies it to the real DOM in one pass — creating nodes, setting attributes, and inserting them into the page. Your components never touch the DOM directly; React owns that step.
Because component functions run on every render, React relies on them behaving predictably: no directly mutating variables outside the function, and no calling hooks conditionally (hooks are introduced in later lessons, but the rule exists because React matches hook calls to state by the order they run in on every render).
Common Mistakes
Mistake 1: Returning multiple sibling elements without a wrapper
function Bad() {
return (
<h1>Title</h1>
<p>Some text</p>
);
}
A component can only return a single root node. Two adjacent JSX elements with no common parent is a syntax error and will fail to build. Wrap them in a real element or, if you don’t want an extra DOM node, a Fragment:
function Fixed() {
return (
<>
<h1>Title</h1>
<p>Some text</p>
</>
);
}
Mistake 2: Lowercase component names
function greeting() {
return <h1>Hi there</h1>;
}
function App() {
return <greeting />;
}
JSX uses the tag’s capitalization to decide what it means: a lowercase tag is treated as a built-in HTML tag name, not a call to your function. <greeting /> renders as a literal, unstyled <greeting></greeting> HTML element instead of invoking the greeting function, and React logs a warning about an unrecognized tag in development. The fix is to always name components with PascalCase:
function Greeting() {
return <h1>Hi there</h1>;
}
function App() {
return <Greeting />;
}
Mistake 3: Missing key when rendering a list
function BadList({ items }) {
return (
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
);
}
This renders correctly the first time, but React logs a console warning (“Each child in a list should have a unique key prop”) and, once the list can change — items added, removed, or reordered — React can mismatch elements to the wrong data, causing subtle bugs. Always give each item a stable, unique key drawn from the data itself, not the array index if the list can reorder:
function GoodList({ items }) {
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
Best Practices
- Name every component in PascalCase so JSX and your teammates can immediately tell components apart from HTML tags.
- Keep each component focused on one job. If a component’s JSX is getting long or is doing two unrelated things, split it into smaller components.
- Never define one component function inside another component’s body (e.g. defining
RowinsideTable‘s render). It gets recreated on every render, which resets any state and re-mounts the DOM unnecessarily — define components at module scope instead. - Always return a single root element, using a Fragment (
<>...</>) when you don’t want an extra wrapping DOM node. - Give a stable, unique
keyprop to every element produced from an array — use an id from your data, not the array index, whenever the list can be reordered, filtered, or have items inserted. - Treat props as read-only input, the same way you’d treat a function’s parameters — a component should never reassign or mutate the props object it receives.
- One component (or a small, tightly related group) per file, exported with a clear default export, makes components easy to find and reuse.
Practice Exercises
- Write a
RecipeCardcomponent that acceptstitle,minutes, anddifficultyprops and renders them inside a labeled card. Then render three<RecipeCard />elements with different prop values inside anAppcomponent. - You’re given an array of book objects (each with
id,title, andauthor). Write aBookListcomponent that maps over the array and renders oneBookcomponent per entry, remembering to set a correctkey. - Find and fix the bug: a component named
navbaris defined and used as<navbar />, but nothing shows up styled as expected. Explain why, then rewrite it correctly.
Summary
- A component is a JavaScript function, named in PascalCase, that returns JSX describing a piece of UI.
- JSX compiles to React elements — plain objects describing type and props — which together form the virtual DOM tree.
- React renders a component tree by calling each component function recursively, then commits the resulting structure to the real DOM.
- Components compose: a component can render other components, letting you build complex UIs from small, reusable pieces.
- A component must return exactly one root node; use a Fragment to group siblings without adding an extra DOM element.
- Every element rendered from an array needs a unique, stable
keyprop. - Keep components small, pure, and defined at module scope — never nested inside another component’s function body.
