---
title: "01 — Components and JSX — The Building Blocks React Asks For"
uid: components-and-jsx
tags: ["jsx", "react", "roadmap:react", "components", "fundamentals", "composition"]
excerpt: "A component is a function that returns JSX, and the whole app is a tree of these functions talking through props. No magic — just function calls returning descriptions of UI."
date: 2026-08-13T03:27:47+0000
source: https://www.aveshina.my.id/en/blog/components-and-jsx
---

"Just components" was how I hand-waved React's core unit, and the vagueness leaked into everything else. The idea that pinned it down: **a component is a function that returns JSX, and the entire app is a tree of these functions talking to each other through props.** [1] Once I stopped thinking "React magic" and started thinking "function calls that return descriptions of UI," most of the API made sense on its own.

## JSX: HTML you can write inside JavaScript

JSX looks like HTML inside JavaScript, and that resemblance is the whole point — it lets me describe what a piece of UI looks like using familiar markup, right next to the logic that produces it [1][2]. Two things keep it from being literal HTML:

- It compiles down to React.createElement(...) calls — it's syntactic sugar, a shorthand for something I could write out the long way, not a string template.
- The DOM attributes are camelCased (className, not class; htmlFor, not for), because JSX is closer to JavaScript than to HTML.

Curly braces {} are the escape hatch: anything inside them is evaluated as a JavaScript expression. {user.name}, {items.length}, {cond ? 'on' : 'off'} all drop straight into the markup. The way of thinking is "JSX is a value," not "JSX is a template" — and because it's a value, I can put it in a variable, return it from a function, pass it around.

```
function Greeting({ name }) {
  // the function returns a JSX expression — that's the whole component
  return <h1 className="title">Hello, {name}</h1>;
}
```

## The function is the component

A functional component is literally a JavaScript function that accepts props as its argument and returns JSX [1][3]. No class, no lifecycle ceremony — just input in, description out. That single contract is why I now default to functional components everywhere; the class form exists for legacy, and the official guidance points at functions.

From that one contract, three corollaries I had to internalize:

- **Components compose by nesting.** Putting <Greeting /> inside <App /> is how the tree is built — a component's JSX can reference other components, recursively [1].
- **Props are read-only inputs.** Whatever the parent passes in, the child reads; the child never mutates props [1][5].
- **Same props in, same JSX out.** A component is a pure function of its props and state. That purity is what makes the tree predictable.

## Lists, keys, and why React needs a stable identity

Rendering a list is just array.map(item => <Row ... />). The gotcha is the key prop — React needs a stable, unique identifier on each repeated element to track which item is which across renders [4]. Without a key, React falls back to array index, and that goes wrong the moment list order changes (reorders, inserts, deletes produce subtle state-leak bugs).

```
{items.map(item => (
  <li key={item.id}>{item.label}</li>
))}
```

The rule I follow: **key comes from the data's identity, never from array position**, unless the list is truly static and never reordered. Using the index as key is the single most common footgun in list rendering, and it's invisible until something reorders.

## Conditional rendering is just JavaScript

There's no v-if or *ngIf directive. Conditional rendering is plain JavaScript operators producing JSX or not [6]:

- && for "render this or nothing": {isLoading && <Spinner />}
- ternary ? : for "render A or B": {user ? <Dashboard /> : <Login />}
- early return null inside the component body for "render nothing"

This is the payoff of "JSX is a value" — the same expressions I'd write in any JavaScript function control what renders. No special syntax to learn.

## Events: functions passed, not strings

Handling events mirrors the DOM, with two JSX-isms: event names are camelCased (onClick, not onclick), and I pass an actual function as the handler rather than a string of code [7]. The handler receives a React **Synthetic Event** — a thin wrapper around the native event that normalizes behavior across browsers.

```
function Button() {
  const handleClick = (e) => {
    e.preventDefault();
    // ...
  };
  return <button onClick={handleClick}>Save</button>;
}
```

## Composition over inheritance, and the patterns that came with it

React's official line is unambiguous: **use composition, not inheritance, to share code between components** [8]. The most useful composition primitive is the children prop — a parent receives whatever JSX sits between its tags, and can wrap it, position it, or pass it through. That's how layout shells, cards, and modals are built.

Two older code-sharing patterns ride on top of composition, and the roadmap lists both, but both are mostly retired in modern code:

- **Render props.** A component accepts a function as a prop and calls it to decide what to render, inverting control — handing the "what to render" decision back to the caller [9]. It's a clean pattern, but hooks largely replaced it for logic reuse.
- **Higher-Order Components (HOC).** A function that takes a component and returns a new, wrapped component — withAuth(Component) [10]. HOCs stack awkwardly, obscure the data flow, and fight TypeScript. The roadmap itself notes they're uncommon now; hooks took the job.

```figure
<svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="A component tree: App at the top returns JSX, branching into Header, Sidebar, and List. Arrows from App to each child are labeled props (data flowing down). List renders three Row children, each receiving an item prop. The whole tree is a set of functions returning JSX.">
  <defs>
    <marker id="carrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <!-- App -->
    <rect x="300" y="24" width="120" height="48" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="360" y="44" font-size="13" font-weight="700" fill="#1e1b4b" text-anchor="middle">App()</text>
    <text x="360" y="60" font-size="9.5" fill="#475569" text-anchor="middle">returns JSX tree</text>

    <!-- children -->
    <rect x="60" y="110" width="110" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="115" y="137" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Header</text>

    <rect x="205" y="110" width="110" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="260" y="137" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Sidebar</text>

    <rect x="350" y="110" width="110" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="405" y="137" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">List</text>

    <!-- List rows -->
    <rect x="500" y="110" width="70" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="535" y="137" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">Row</text>
    <rect x="580" y="110" width="70" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="615" y="137" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">Row</text>
    <rect x="660" y="110" width="50" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="685" y="137" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">Row</text>

    <!-- connectors from App -->
    <path d="M330,72 C200,90 115,95 115,108" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>
    <path d="M350,72 C300,90 260,95 260,108" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>
    <path d="M380,72 C390,90 405,95 405,108" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>

    <!-- connectors from List to rows -->
    <path d="M460,132 L498,132" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>
    <path d="M460,138 C500,150 580,150 580,138" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>
    <path d="M460,144 C500,165 660,165 660,144" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#carrow)"/>

    <!-- props label -->
    <text x="250" y="92" font-size="10" fill="#64748b" text-anchor="middle" font-style="italic">props flow down the tree</text>

    <!-- caption -->
    <text x="360" y="250" font-size="11" fill="#475569" text-anchor="middle" font-style="italic">each node is a function returning JSX; data flows down via props</text>
  </g>
</svg>
```

The diagram is the way of thinking in one frame: an app is a tree of functions, each returning JSX, each receiving data from its parent through props. Composition is just calling those functions; inheritance never enters the picture.

## How I use this

When I scaffold any new screen, I sketch the tree first — what's the top component, what are its children, what props cross each edge — before writing a line of JSX. That habit forces the data flow to be obvious up front. The other rules I keep by default: functional components only, key from a stable id (never the index), conditional rendering with &&/ternary (never a special directive), and composition through children rather than HOCs. The rare time I reach for a render prop, it's for a genuinely inverted-control case like a virtualized list; for ordinary logic reuse, a custom hook is what I write instead.

## References

[1] React team, "Quick Start," react.dev, 2024. [Online]. Available: [https://react.dev/learn](https://react.dev/learn)

[2] React team, "Writing markup with JSX," react.dev, 2024. [Online]. Available: [https://react.dev/learn/writing-markup-with-jsx](https://react.dev/learn/writing-markup-with-jsx)

[3] R. Wieruch, "The difference between components, elements, and instances," robinwieruch.de, 2023. [Online]. Available: [https://www.robinwieruch.de/react-element-component/](https://www.robinwieruch.de/react-element-component/)

[4] React team, "Rendering lists: keeping list items in order with key," react.dev, 2024. [Online]. Available: [https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key)

[5] React team, "Passing props to a component," react.dev, 2024. [Online]. Available: [https://react.dev/learn/passing-props-to-a-component](https://react.dev/learn/passing-props-to-a-component)

[6] React team, "Conditional rendering," react.dev, 2024. [Online]. Available: [https://react.dev/learn/conditional-rendering](https://react.dev/learn/conditional-rendering)

[7] React team, "Responding to events," react.dev, 2024. [Online]. Available: [https://react.dev/learn/responding-to-events](https://react.dev/learn/responding-to-events)

[8] React team, "Passing JSX as children," react.dev, 2024. [Online]. Available: [https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children](https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children)

[9] patterns.dev, "Render Props Pattern," 2023. [Online]. Available: [https://www.patterns.dev/posts/render-props-pattern/](https://www.patterns.dev/posts/render-props-pattern/)

[10] React team, "Higher-Order Components," legacy docs. [Online]. Available: [https://reactjs.org/docs/higher-order-components.html](https://reactjs.org/docs/higher-order-components.html)

```quiz
Q: What is a React functional component, at its simplest?
- A JavaScript function that receives props and returns JSX
- A class extending React.Component with a render method
- An HTML template compiled to the DOM
correct: 0
explain: A functional component is a plain JavaScript function: props in, JSX out. The class form exists for legacy code but is no longer the default.

Q: Why must list items have a stable `key` prop instead of the array index?
- So React can track each item's identity across reorders and inserts
- To make the elements render faster
- Because JSX requires every attribute to be present
correct: 0
explain: React uses the key to match elements between renders. Index-based keys break when list order changes, leaking state between items.

Q: In JSX, how do you embed a JavaScript expression inside markup?
- Wrap it in single quotes
- Wrap it in curly braces {}
- Prefix it with a $ symbol
correct: 1
explain: Curly braces switch into JavaScript mode inside JSX; whatever is inside is evaluated as an expression.

Q: React event handlers differ from native DOM handlers in that…
- event names are camelCased and you pass a function, not a string
- you must call addEventListener manually
- events only fire on the server
correct: 0
explain: JSX uses camelCase (onClick) and you pass a handler function directly, rather than a string of code like in plain HTML.

Q: For sharing reusable logic in modern React, the recommended approach is…
- Higher-Order Components
- custom hooks (composition + hooks)
- class inheritance
correct: 1
explain: HOCs and render props are mostly retired. Custom hooks extract reusable logic cleanly, and composition (the children prop) handles UI structure sharing.
```
