# Components

Course: [React](https://www.devtutorials.ai/learn/react) · Section: React Fundamentals
Source: https://www.devtutorials.ai/learn/react/react-fundamentals/components

> What components are, functional components, creating and typing components with props, rendering, composition, reusable components, importing and exporting, naming rules, smart vs presentational components, and folder structure.

## What Are Components?

A **component** is a small, reusable piece of a web page. A button, a book card and a menu can each be a component.

You build each piece once. Then you join the pieces together to make the whole page.

### Real-World Example

An online bookshop shows 40 books on its home page. The team writes one `BookCard` component with the cover, title, price and an **Add to cart** button, and uses it 40 times.

When the designer moves the price above the title, the team changes `BookCard` once, and all 40 cards change.

<figure className="react-fig">
<svg viewBox="0 0 520 230" role="img" aria-labelledby="fig-page-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-page-title">A web page split into components: Header, BookCard three times, and Footer</title>
  <rect x="10" y="10" width="500" height="210" rx="10" style={{ fill: "var(--panel)", stroke: "var(--line)", strokeWidth: 2 }} />
  <text x="24" y="32" style={{ fill: "var(--muted)", fontSize: 12, fontFamily: "var(--sans)" }}>App (the whole page)</text>
  <rect x="24" y="42" width="472" height="36" rx="6" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="260" y="65" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 14, fontWeight: 600, fontFamily: "var(--sans)" }}>Header</text>
  <rect x="24" y="88" width="148" height="78" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="98" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 14, fontWeight: 600, fontFamily: "var(--sans)" }}>BookCard</text>
  <rect x="186" y="88" width="148" height="78" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="260" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 14, fontWeight: 600, fontFamily: "var(--sans)" }}>BookCard</text>
  <rect x="348" y="88" width="148" height="78" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="422" y="131" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 14, fontWeight: 600, fontFamily: "var(--sans)" }}>BookCard</text>
  <rect x="24" y="176" width="472" height="32" rx="6" style={{ fill: "var(--panel2)", stroke: "var(--faint)", strokeWidth: 1.5 }} />
  <text x="260" y="197" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 14, fontWeight: 600, fontFamily: "var(--sans)" }}>Footer</text>
</svg>
<figcaption>One page, four kinds of components. BookCard is written once and used three times.</figcaption>
</figure>

A component is a reusable piece of the page. You build it once and use it wherever you need it.

## Functional Components

A **functional component** is a JavaScript function that returns TSX. React calls the function and shows what it returns.

This is the standard way to write components today. Older React code also uses **class components**, but new code uses functions.

```tsx
function Greeting() {
  return <h2>Welcome, reader!</h2>;
}
```

## Creating Components

To create a component, follow three steps:

1. Write a function whose name starts with a capital letter.
2. Return TSX from the function.
3. Use it like a tag: `<Greeting />`.

      <h1>Book Corner</h1>
    </div>
  );
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

`<Greeting />` is used twice, so the greeting appears twice. You can also write components as arrow functions: `const Greeting = () => <h2>Welcome, reader!</h2>;`

## Creating Typed Components

A component can receive values from outside. These values are called **props** (short for properties). They work like the inputs of a function.

In TypeScript, you describe the props with a **type**. The type lists each prop and the kind of value it holds. TypeScript then warns you if a prop is missing or has the wrong kind of value.

      <h3>{title}</h3>
      <p>by {author}</p>
      <p>₹{price} · {inStock ? "In stock" : "Out of stock"}</p>
    </div>
  );
}
`,
    "App.tsx": `import BookCard from "./BookCard";

  return (
    <div>
    </div>
  );
}
`,
    "App.css": `.card { border: 1px solid #cbd5e1; border-radius: 8px; padding: 2px 12px; margin-bottom: 8px; }
.card h3 { margin: 8px 0 4px; }
.card p { margin: 4px 0; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Here is what each part does:

- `type BookCardProps` lists the props: `title` and `author` are text, `price` is a number.
- `inStock?` has a `?`, so it is optional. `inStock = true` gives it a default value.
- `{ title, author, price, inStock }` takes the props out of the object so you can use them by name.

Text props use quotes: `title="The Alchemist"`. Other values use braces: `price={299}`, `inStock={false}`.

In your own editor, try removing `price={299}`. TypeScript underlines the line, because `price` is required.

## Rendering Components

**Rendering** a component means showing it on the page. You render a component by writing it as a tag inside TSX.

React then calls the component function and puts the returned TSX on the page. The top component, usually `App`, is rendered once in `main.tsx`:

```tsx
createRoot(document.getElementById("root")!).render(<App />);
```

Every other component is rendered by a component above it. A component can also render nothing by returning `null`:

```tsx
function SaleBanner({ onSale }: { onSale: boolean }) {
  if (!onSale) return null;
  return <p>Big sale today!</p>;
}
```

## Component Composition

**Composition** means using one component inside another component. For example, `App` uses `Header`, `BookList` and `Footer` inside it.

When components are used inside each other, they form a **component tree**. `App` is at the top of the tree.

- The component on the outside is the **parent**.
- The component used inside it is the **child**.

So `App` is the parent of `BookList`. `BookList` is a child of `App`.

<figure className="react-fig">
<svg viewBox="0 0 520 210" role="img" aria-labelledby="fig-tree-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-tree-title">Component tree: App has children Header, BookList and Footer. BookList has three BookCard children.</title>
  <g style={{ stroke: "var(--faint)", strokeWidth: 1.5, fill: "none" }}>
    <path d="M260 46 L90 84" /><path d="M260 46 L260 84" /><path d="M260 46 L430 84" />
    <path d="M260 116 L150 154" /><path d="M260 116 L260 154" /><path d="M260 116 L370 154" />
  </g>
  <g style={{ fontFamily: "var(--sans)", fontSize: 13, fontWeight: 600 }}>
    <rect x="210" y="14" width="100" height="32" rx="6" style={{ fill: "var(--accent)" }} />
    <text x="260" y="35" textAnchor="middle" style={{ fill: "#fff" }}>App</text>
    <rect x="40" y="84" width="100" height="32" rx="6" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)" }} />
    <text x="90" y="105" textAnchor="middle" style={{ fill: "var(--accent)" }}>Header</text>
    <rect x="210" y="84" width="100" height="32" rx="6" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)" }} />
    <text x="260" y="105" textAnchor="middle" style={{ fill: "var(--accent)" }}>BookList</text>
    <rect x="380" y="84" width="100" height="32" rx="6" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)" }} />
    <text x="430" y="105" textAnchor="middle" style={{ fill: "var(--accent)" }}>Footer</text>
    <rect x="105" y="154" width="90" height="32" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)" }} />
    <text x="150" y="175" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>BookCard</text>
    <rect x="215" y="154" width="90" height="32" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)" }} />
    <text x="260" y="175" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>BookCard</text>
    <rect x="325" y="154" width="90" height="32" rx="6" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)" }} />
    <text x="370" y="175" textAnchor="middle" style={{ fill: "var(--accent-secondary)" }}>BookCard</text>
  </g>
</svg>
<figcaption>The component tree of the lab below. App is the parent of Header, BookList and Footer.</figcaption>
</figure>

    </div>
  );
}
`,
    "Header.tsx": `export default function Header() {
  return <h1>Book Corner</h1>;
}
`,
    "BookList.tsx": `import BookCard from "./BookCard";

  return (
    <div>
    </div>
  );
}
`,
    "BookCard.tsx": `type BookCardProps = { title: string; price: number };

  return <p>{title} — ₹{price}</p>;
}
`,
    "Footer.tsx": `export default function Footer() {
  return <small>Open every day, 9 am to 9 pm.</small>;
}
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Click through the tabs. `App` does not know how a book card looks. It only puts `Header`, `BookList` and `Footer` in order. Each component has one job.

## Reusable Components

A **reusable component** is written once and used in many places, with different props each time. This saves you from copying the same code.

      {label}
    </button>
  );
}
`,
    "App.tsx": `import Button from "./Button";

  return (
    <div>
      <h2>Wings of Fire</h2>
    </div>
  );
}
`,
    "App.css": `.btn { color: #fff; border: 0; border-radius: 6px; padding: 6px 14px; }
.btn-blue { background: #0047ba; }
.btn-green { background: #15803d; }
.btn-red { background: #dc2626; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

Press each button and watch the console. All three come from one `Button` component.

`color?: "blue" | "green" | "red"` means `color` can only be one of these three words. If you write `color="pink"`, TypeScript warns you.

## Exporting Components

To use a component in another file, you must **export** it first. There are two kinds of export.

### Default Export

A file can have **one** default export. It is usually the main component of the file.

```tsx
// Header.tsx
export default function Header() {
  return <h1>Book Corner</h1>;
}
```

### Named Export

A file can have **many** named exports. Each one is exported by its name.

```tsx
// Badges.tsx
export function NewBadge() {
  return <span>New</span>;
}

export function SaleBadge() {
  return <span>Sale</span>;
}
```

## Importing Components

To use an exported component, **import** it at the top of the file. How you import depends on how it was exported.

- **Default export** — no curly braces: `import Header from "./Header";`
- **Named export** — curly braces, and the exact name: `import { NewBadge, SaleBadge } from "./Badges";`

The path `"./Header"` means "the file `Header.tsx` in this same folder". You do not write the `.tsx` ending.

      <p>Atomic Habits <NewBadge /></p>
      <p>The Alchemist <SaleBadge /></p>
    </div>
  );
}
`,
    "Header.tsx": `export default function Header() {
  return <h1>Book Corner</h1>;
}
`,
    "Badges.tsx": `export function NewBadge() {
  return <span className="badge new">New</span>;
}

  return <span className="badge sale">Sale</span>;
}
`,
    "App.css": `.badge { font-size: 12px; color: #fff; border-radius: 999px; padding: 2px 8px; }
.new { background: #0047ba; }
.sale { background: #f57c00; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<App />);
`,
  }}
/>

<div className="tscroll">

| | Default export | Named export |
|---|---|---|
| How many per file | One | Many |
| Export | `export default function Header()` | `export function NewBadge()` |
| Import | `import Header from "./Header"` | `import { NewBadge } from "./Badges"` |
| Name when importing | Any name you like | Must match the exported name |

</div>

Default export → import without braces. Named export → import with braces and the exact name.

## Component Naming Conventions

Follow these naming rules so your code is easy to read:

- **Start with a capital letter** — `BookCard`, not `bookCard`. React treats lower-case tags as HTML tags.
- **Use PascalCase** — Join words and capitalise each one: `ShoppingCart`, `UserProfile`.
- **Name the file after the component** — `BookCard` lives in `BookCard.tsx`.
- **Use a clear noun** — `BookList` says what it shows. `Thing` or `Comp1` does not.
- **Name props types after the component** — `BookCardProps` for `BookCard`.

## Smart vs Presentational Components

You can split components into two kinds, based on the work they do.

- A **smart component** has the data. It chooses which data to show. It is also called a **container** component.
- A **presentational component** does not have its own data. It gets the data through props and shows it on the screen.

In the example below, `BookShelf` is the smart component. It has a list of books and picks only the books that are in stock. `BookTable` is the presentational component. It shows those books in a table.

<figure className="react-fig">
<svg viewBox="0 0 520 150" role="img" aria-labelledby="fig-smart-title" style={{ width: "100%", maxWidth: 520, height: "auto" }}>
  <title id="fig-smart-title">Data flows from the smart component BookShelf to the presentational component BookTable through props</title>
  <rect x="10" y="30" width="190" height="90" rx="10" style={{ fill: "var(--accent-soft)", stroke: "var(--accent)", strokeWidth: 1.5 }} />
  <text x="105" y="62" textAnchor="middle" style={{ fill: "var(--accent)", fontSize: 15, fontWeight: 700, fontFamily: "var(--sans)" }}>BookShelf</text>
  <text x="105" y="84" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>Smart: has the data,</text>
  <text x="105" y="101" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>picks books in stock</text>
  <rect x="320" y="30" width="190" height="90" rx="10" style={{ fill: "var(--accent-secondary-soft)", stroke: "var(--accent-secondary)", strokeWidth: 1.5 }} />
  <text x="415" y="62" textAnchor="middle" style={{ fill: "var(--accent-secondary)", fontSize: 15, fontWeight: 700, fontFamily: "var(--sans)" }}>BookTable</text>
  <text x="415" y="84" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>Presentational: only</text>
  <text x="415" y="101" textAnchor="middle" style={{ fill: "var(--ink2)", fontSize: 12, fontFamily: "var(--sans)" }}>shows what it gets</text>
  <path d="M204 75 L312 75" style={{ stroke: "var(--faint)", strokeWidth: 2, fill: "none" }} />
  <path d="M304 68 L314 75 L304 82 Z" style={{ fill: "var(--faint)" }} />
  <text x="260" y="64" textAnchor="middle" style={{ fill: "var(--muted)", fontSize: 12, fontFamily: "var(--mono)" }}>props</text>
  <circle r="6" style={{ fill: "var(--accent)" }}>
    <animateMotion dur="2s" repeatCount="indefinite" path="M204 75 L312 75" />
  </circle>
</svg>
<figcaption>Data moves one way: from the smart component down to the presentational one, through props.</figcaption>
</figure>

      <thead>
        <tr><th>Title</th><th>Price</th></tr>
      </thead>
      <tbody>
        {books.map((book) => (
          <tr key={book.title}>
            <td>{book.title}</td>
            <td>₹{book.price}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
`,
    "App.css": `table { border-collapse: collapse; }
th, td { border: 1px solid #cbd5e1; padding: 6px 12px; text-align: left; }
`,
    "main.tsx": `import { createRoot } from "react-dom/client";

createRoot(document.getElementById("root")!).render(<BookShelf />);
`,
  }}
/>

The table shows only two books. "Wings of Fire" is out of stock, so `BookShelf` did not send it.

`BookTable` does not check the stock. It shows any list of books you give it. So you can use `BookTable` again for other lists.

`books.map(...)` creates one table row for each book. You will learn lists in a later lesson.

You do not have to split every component this way. In a small app, one component can do both jobs.

## Component Folder Structure

When an app has many components, keep them in a `components` folder inside `src`. Put each component in its own file.

```text
src/
├── components/
│   ├── Header.tsx
│   ├── Footer.tsx
│   ├── Button.tsx
│   └── books/
│       ├── BookCard.tsx
│       ├── BookList.tsx
│       └── BookCard.css
├── pages/
│   ├── HomePage.tsx
│   └── CartPage.tsx
├── App.tsx
└── main.tsx
```

- **components/** — Small, reusable pieces such as buttons and cards.
- **components/books/** — Components that belong together can share a sub-folder.
- **pages/** — One component for each screen of the app.
- A component's CSS file sits next to it, with the same name.

Import paths follow the folders. From `App.tsx`, write `import Header from "./components/Header";`.

Easy Way to Remember: one component, one file, same name.

## Common mistakes

<div className="tscroll">

| Mistake | What you see | Fix |
|---|---|---|
| Component name starts with a small letter: `function bookCard()` | Nothing shows, and the browser treats it as an unknown tag | Start with a capital letter: `BookCard` |
| Forgetting `export` in the component file | `Element type is invalid` error on the page | Add `export default` or `export` |
| Importing a named export without braces | The imported component is `undefined` | Use `import { NewBadge } from "./Badges"` |
| Using braces for a default export | The imported component is `undefined` | Use `import Header from "./Header"` |
| Calling a component like a function: `{BookCard()}` | It may work at first, but causes errors later | Use it as a tag: `<BookCard />` |
| Forgetting `return` in the component | Nothing shows on the page | Add `return` before the TSX |
| Missing a required prop | TypeScript warns that the prop is missing | Pass the prop, or make it optional with `?` |
| Writing a number prop in quotes: `price="299"` | TypeScript warns: string is not a number | Use braces: `price={299}` |

</div>

## Check Yourself

## Exercises

## What to Remember

- A component is a function that returns TSX. Its name starts with a capital letter.
- Describe props with a type, and build pages by putting small components together.
- Export a component to use it in another file: default export without braces, named export with braces.

Next: Props
