|$ curl https://forge-ai.dev/api/markdown?path=docs/react/jsx
$cat docs/react-—-jsx-deep-dive.md
updated Last week·16 min read·published

React — JSX Deep Dive

ReactBeginner to Intermediate🎯Free Tools
What is JSX?

JSX is a syntax extension for JavaScript that lets you write HTML-like code inside your JavaScript. It is not valid JavaScript on its own — compilers like Babel or SWC transform JSX into regular function calls before your code runs. React uses this transformation to create virtual DOM elements.

JSX produces React elements — plain objects describing what should appear on screen. Understanding how JSX compiles helps you write more predictable code and debug rendering issues.

JSX Compilation
jsx_compilation.tsx
TypeScript
1// JSX you write:
2const element = <h1 className="title">Hello, world!</h1>;
3
4// What Babel/SWC compiles it to:
5const element = React.createElement("h1", { className: "title" }, "Hello, world!");
6
7// createElement returns a plain object:
8// {
9// type: "h1",
10// props: {
11// className: "title",
12// children: "Hello, world!"
13// },
14// key: null,
15// ref: null
16// }
17
18// Multiple children:
19const list = (
20 <ul>
21 <li>First</li>
22 <li>Second</li>
23 </ul>
24);
25
26// Compiles to:
27const list = React.createElement(
28 "ul",
29 null,
30 React.createElement("li", null, "First"),
31 React.createElement("li", null, "Second")
32);
33
34// Custom components — type is the component function/class
35const app = <App title="Dashboard" />;
36
37// Compiles to:
38const app = React.createElement(App, { title: "Dashboard" });
39
40// JSX must have a single root element
41// ❌ Invalid:
42// return (
43// <h1>Title</h1>
44// <p>Paragraph</p>
45// );
46
47// ✅ Valid — wrap in a div or fragment:
48return (
49 <>
50 <h1>Title</h1>
51 <p>Paragraph</p>
52 </>
53);

info

JSX compiles to React.createElement() calls. With the automatic JSX runtime in modern React (17+), you don't need to import Reactin every file — it's injected automatically.
Expressions in JSX
expressions.tsx
TSX
1// Curly braces {} embed any JavaScript expression in JSX
2
3// Variables
4const name = "Alice";
5const element = <h1>Hello, {name}</h1>;
6
7// Function calls
8function formatDate(date: Date): string {
9 return date.toLocaleDateString();
10}
11const dateElement = <p>Today is {formatDate(new Date())}</p>;
12
13// Arithmetic
14const price = 19.99;
15const tax = 0.08;
16const total = <span>Total: ${(price * (1 + tax)).toFixed(2)}</span>;
17
18// Ternary expressions
19const isLoggedIn = true;
20const greeting = <h1>{isLoggedIn ? "Welcome back!" : "Please sign in"}</h1>;
21
22// Template literals
23const user = "Alice";
24const role = "Admin";
25const header = <h2>{`${role}: ${user}`}</h2>;
26
27// Object spread in props
28const baseProps = { className: "card", id: "main-card" };
29const card = <div {...baseProps} data-testid="card-1" />;
30
31// Array methods — map returns arrays of elements
32const items = ["React", "TypeScript", "Next.js"];
33const list = (
34 <ul>
35 {items.map((item) => (
36 <li key={item}>{item}</li>
37 ))}
38 </ul>
39);
40
41// Children as expressions
42function Card({ children }: { children: React.ReactNode }) {
43 return <div className="card">{children}</div>;
44}
45
46const cardWithContent = (
47 <Card>
48 <h2>Title</h2>
49 <p>{someCondition ? "Yes" : "No"}</p>
50 </Card>
51);
52
53// Inline styles — object, not string
54const style = { color: "red", fontSize: "16px" };
55const colored = <p style={style}>Red text</p>;
Fragments
fragments.tsx
TSX
1// Fragments let you group children without adding extra DOM nodes
2
3// Short syntax
4function List() {
5 return (
6 <>
7 <dt>Term 1</dt>
8 <dd>Definition 1</dd>
9 <dt>Term 2</dt>
10 <dd>Definition 2</dd>
11 </>
12 );
13}
14
15// Explicit Fragment syntax — needed when you need a key prop
16import { Fragment } from "react";
17
18function Glossary({ items }: { items: { term: string; def: string }[] }) {
19 return (
20 <dl>
21 {items.map((item) => (
22 <Fragment key={item.term}>
23 <dt>{item.term}</dt>
24 <dd>{item.def}</dd>
25 </Fragment>
26 ))}
27 </dl>
28 );
29}
30
31// Fragment does NOT accept props (no key, className, style)
32// ❌ Invalid:
33// <><h1 className="title">Title</h1></>
34
35// ✅ Use a div or a wrapper component instead:
36// <div className="title"><h1>Title</h1></div>
37
38// Custom Fragment wrapper for layout
39function Row({ children }: { children: React.ReactNode }) {
40 return <div style={{ display: "flex", gap: "1rem" }}>{children}</div>;
41}
42
43function Layout() {
44 return (
45 <Row>
46 <aside>Sidebar</aside>
47 <main>Content</main>
48 </Row>
49 );
50}
51
52// Fragment in table rows — common use case
53function Table({ rows }: { rows: { name: string; age: number }[] }) {
54 return (
55 <table>
56 <tbody>
57 {rows.map((row) => (
58 <Fragment key={row.name}>
59 <tr>
60 <td>{row.name}</td>
61 <td>{row.age}</td>
62 </tr>
63 </Fragment>
64 ))}
65 </tbody>
66 </table>
67 );
68}
Conditional Rendering
conditional.tsx
TSX
1// Multiple patterns for conditional rendering
2
3// 1. if/else — simplest, good for complex conditions
4function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) {
5 if (isLoggedIn) {
6 return <h1>Welcome back!</h1>;
7 }
8 return <h1>Please sign in.</h1>;
9}
10
11// 2. Ternary — inline conditional
12const message = <p>{isLoggedIn ? "Welcome" : "Sign in"}</p>;
13
14// 3. Logical AND — render or nothing
15const notification = hasNotification && <Badge count={count} />;
16
17// 4. Variable assignment — for complex JSX
18function Status({ status }: { status: "loading" | "error" | "success" }) {
19 let content: React.ReactNode;
20
21 if (status === "loading") {
22 content = <Spinner />;
23 } else if (status === "error") {
24 content = <ErrorMessage />;
25 } else {
26 content = <SuccessMessage />;
27 }
28
29 return <div className="status">{content}</div>;
30}
31
32// 5. Early return — guard clause
33function UserCard({ user }: { user: User | null }) {
34 if (!user) return null;
35
36 return (
37 <div>
38 <h2>{user.name}</h2>
39 <p>{user.email}</p>
40 </div>
41 );
42}
43
44// 6. Render props pattern — composition over conditionals
45function Maybe({
46 condition,
47 children,
48}: {
49 condition: boolean;
50 children: React.ReactNode;
51}) {
52 return condition ? <>{children}</> : null;
53}
54
55// Usage
56<Maybe condition={isLoggedIn}>
57 <Dashboard />
58</Maybe>
59
60// 7. Switch-like pattern with object mapping
61type Status = "idle" | "loading" | "success" | "error";
62
63const statusComponents: Record<Status, React.FC> = {
64 idle: IdleScreen,
65 loading: LoadingSpinner,
66 success: SuccessScreen,
67 error: ErrorScreen,
68};
69
70function StatusView({ status }: { status: Status }) {
71 const Component = statusComponents[status];
72 return <Component />;
73}
Lists and Keys
lists_keys.tsx
TSX
1// Keys help React identify which items changed, added, or removed
2
3// ✅ Good — use stable, unique IDs
4interface Todo {
5 id: string;
6 text: string;
7 completed: boolean;
8}
9
10function TodoList({ todos }: { todos: Todo[] }) {
11 return (
12 <ul>
13 {todos.map((todo) => (
14 <li key={todo.id}>
15 <input type="checkbox" checked={todo.completed} />
16 {todo.text}
17 </li>
18 ))}
19 </ul>
20 );
21}
22
23// ❌ Bad — using index as key (causes issues with reorder/delete)
24// {todos.map((todo, index) => (
25// <li key={index}>{todo.text}</li>
26// ))}
27
28// Why index keys are bad:
29// If you delete item 1 from [A, B, C] → [A, C]
30// React sees key 0 (A→A, skip), key 1 (B→C, update), key 2 (removed)
31// But C didn't change — B was deleted. React updates the wrong element.
32
33// Nested lists — flatten or use compound keys
34interface Category {
35 id: string;
36 name: string;
37 items: { id: string; name: string }[];
38}
39
40function CategoryList({ categories }: { categories: Category[] }) {
41 return (
42 <div>
43 {categories.map((category) => (
44 <div key={category.id}>
45 <h3>{category.name}</h3>
46 <ul>
47 {category.items.map((item) => (
48 <li key={`${category.id}-${item.id}`}>{item.name}</li>
49 ))}
50 </ul>
51 </div>
52 ))}
53 </div>
54 );
55}
56
57// Filtering and reordering with keys
58function FilteredList({ items }: { items: string[] }) {
59 const [filter, setFilter] = React.useState("");
60
61 const filtered = items.filter((item) =>
62 item.toLowerCase().includes(filter.toLowerCase())
63 );
64
65 return (
66 <div>
67 <input value={filter} onChange={(e) => setFilter(e.target.value)} />
68 <ul>
69 {filtered.map((item) => (
70 <li key={item}>{item}</li>
71 ))}
72 </ul>
73 </div>
74 );
75}

warning

Never use array indices as keys when the list can be reordered, filtered, or have items inserted/deleted. Use stable unique identifiers from your data instead.
Spread Attributes
spread.tsx
TSX
1// Spread syntax passes an object's properties as props
2
3// Base props object
4const buttonProps = {
5 type: "submit" as const,
6 className: "btn btn-primary",
7 disabled: false,
8};
9
10// Spread into JSX
11<button {...buttonProps}>Submit</button>
12// Compiles to: React.createElement("button", { ...buttonProps }, "Submit")
13
14// Override spread props — later props win
15<button {...buttonProps} disabled={true} className="btn-danger">
16 Delete
17</button>
18// type="submit", className="btn-danger" (overridden), disabled=true (overridden)
19
20// Forward all props to a child component
21function TextInput(props: React.InputHTMLAttributes<HTMLInputElement>) {
22 return <input {...props} />;
23}
24
25// Usage
26<TextInput
27 type="email"
28 placeholder="Enter email"
29 className="input-field"
30 onChange={(e) => console.log(e.target.value)}
31/>
32
33// Spread with conditional props
34interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
35 variant?: "primary" | "secondary" | "danger";
36}
37
38function Button({ variant = "primary", className, ...rest }: ButtonProps) {
39 return (
40 <button
41 className={`btn btn-${variant} ${className ?? ""}`.trim()}
42 {...rest}
43 />
44 );
45}
46
47// Props forwarding pattern
48function Dialog({
49 title,
50 content,
51 ...dialogProps
52}: {
53 title: string;
54 content: React.ReactNode;
55} & React.DialogHTMLAttributes<HTMLDialogElement>) {
56 return (
57 <dialog {...dialogProps}>
58 <h2>{title}</h2>
59 <div>{content}</div>
60 </dialog>
61 );
62}
63
64// Spread with refs — use forwardRef
65const FancyInput = React.forwardRef<HTMLInputElement, InputProps>(
66 (props, ref) => <input ref={ref} {...props} className="fancy" />
67);
JSX Prevents Injection
prevents_injection.tsx
TSX
1// JSX escapes values by default — prevents XSS attacks
2
3// User input is rendered as text, not HTML
4const userInput = '<script>alert("xss")</script>';
5const element = <p>{userInput}</p>;
6// Renders: <p>&lt;script&gt;alert("xss")&lt;/script&gt;</p>
7// NOT as executable script
8
9// dangerouslySetInnerHTML — opt-in to raw HTML (use with caution)
10const rawHtml = '<strong>Bold text</strong>';
11
12// ✅ Safe — JSX escapes automatically
13const safe = <div>{rawHtml}</div>;
14// Renders: <div>&lt;strong&gt;Bold text&lt;/strong&gt;</div>
15
16// ⚠️ Dangerous — bypasses escaping
17const dangerous = (
18 <div dangerouslySetInnerHTML={{ __html: rawHtml }} />
19);
20// Renders: <div><strong>Bold text</strong></div>
21
22// Only use dangerouslySetInnerHTML with trusted content:
23function MarkdownRenderer({ content }: { content: string }) {
24 // Sanitize content first with DOMPurify or similar
25 const sanitized = DOMPurify.sanitize(content);
26 return (
27 <article
28 className="prose"
29 dangerouslySetInnerHTML={{ __html: sanitized }}
30 />
31 );
32}
33
34// Attribute values are also escaped
35const url = 'javascript:alert("xss")';
36// ❌ React does NOT prevent javascript: URLs in href
37// <a href={url}>Click</a> — this IS dangerous
38
39// ✅ Always validate URLs before rendering
40function SafeLink({ href, children }: { href: string; children: React.ReactNode }) {
41 const isSafe = href.startsWith("http://") || href.startsWith("https://") || href.startsWith("/");
42 if (!isSafe) {
43 console.warn(`Blocked unsafe URL: ${href}`);
44 return <span>{children}</span>;
45 }
46 return <a href={href}>{children}</a>;
47}

info

JSX automatically escapes all interpolated values, preventing XSS attacks. Never use dangerouslySetInnerHTML with user-provided content without sanitization.
$Blueprint — Engineering Documentation·Section ID: REACT-JSX·Revision: 1.0