20 minlesson

JSX Deep Dive

JSX Deep Dive

Now that you understand JSX basics, let's explore advanced patterns, the compilation process, and best practices for writing clean, maintainable JSX.

How JSX Compiles

JSX is syntactic sugar for React.createElement() calls. Modern React (17+) uses an automatic JSX transform:

jsx
1// What you write
2function Greeting() {
3 return <h1 className="title">Hello!</h1>;
4}
5
6// What it becomes (automatic transform)
7import { jsx as _jsx } from 'react/jsx-runtime';
8
9function Greeting() {
10 return _jsx('h1', { className: 'title', children: 'Hello!' });
11}

The automatic transform means:

  • No need to import React in every file
  • Slightly smaller bundle size
  • Better future optimizations

JSX Element Types

JSX can render different types of elements:

jsx
1// 1. HTML elements (lowercase)
2<div>Native HTML</div>
3
4// 2. Components (PascalCase)
5<MyComponent />
6
7// 3. Dynamic components
8const Component = isAdmin ? AdminPanel : UserPanel;
9<Component />
10
11// 4. Namespaced components
12<Form.Input />
13<Form.Button />

Naming convention matters! React uses the case to distinguish:

  • <button> → HTML element
  • <Button> → React component

Spread Attributes

Pass all props at once using spread:

jsx
1const buttonProps = {
2 className: 'btn btn-primary',
3 disabled: false,
4 onClick: handleClick,
5};
6
7// Spread all props
8<button {...buttonProps}>Click me</button>
9
10// Override specific props
11<button {...buttonProps} disabled={true}>
12 Disabled
13</button>
14
15// Order matters! Later props override earlier ones
16<button disabled={true} {...buttonProps}>
17 Actually not disabled (buttonProps.disabled = false wins)
18</button>

Rest Props Pattern

Accept and forward unknown props:

jsx
1function Button({ variant, children, ...rest }) {
2 const className = `btn btn-${variant}`;
3
4 // Forward all other props to the native button
5 return (
6 <button className={className} {...rest}>
7 {children}
8 </button>
9 );
10}
11
12// Usage - onClick, disabled, etc. are forwarded
13<Button variant="primary" onClick={save} disabled={loading}>
14 Save
15</Button>

Boolean Attributes

In JSX, attributes without values are truthy:

jsx
1// These are equivalent
2<input disabled={true} />
3<input disabled />
4
5// These are equivalent
6<input disabled={false} />
7<input /> // Just omit the attribute

Common boolean attributes:

  • disabled
  • checked
  • readOnly
  • required
  • autoFocus

Inline Styles

JSX styles use objects with camelCase properties:

jsx
1function Box() {
2 const style = {
3 backgroundColor: 'blue', // not background-color
4 fontSize: '16px', // not font-size
5 padding: 20, // numbers assume 'px'
6 marginTop: '1rem',
7 };
8
9 return <div style={style}>Styled box</div>;
10}
11
12// Inline object (double braces)
13<div style={{ color: 'red', fontWeight: 'bold' }}>
14 Red bold text
15</div>

Note: Inline styles have higher specificity but are harder to maintain. Prefer CSS classes for most styling.

Dangerous HTML

To render raw HTML (use sparingly!):

jsx
1function Article({ htmlContent }) {
2 // WARNING: Only use with trusted content!
3 return (
4 <div dangerouslySetInnerHTML={{ __html: htmlContent }} />
5 );
6}

This bypasses React's XSS protection. Only use when:

  • Content comes from a trusted source
  • You've sanitized the HTML
  • There's no other option (like markdown parsers)

Comments in JSX

Use JavaScript comments inside curly braces:

jsx
1function Component() {
2 return (
3 <div>
4 {/* This is a JSX comment */}
5 <h1>Title</h1>
6
7 {/*
8 Multi-line comments
9 work like this
10 */}
11 <p>Content</p>
12 </div>
13 );
14}

Whitespace in JSX

JSX removes whitespace differently than HTML:

jsx
1// These render the same (no space between spans)
2<div><span>A</span><span>B</span></div>
3<div>
4 <span>A</span>
5 <span>B</span>
6</div>
7
8// To add space, use explicit space or CSS
9<div>
10 <span>A</span>{' '}<span>B</span>
11</div>

Expressions vs Statements

JSX only accepts expressions (things that return a value), not statements:

jsx
1// EXPRESSIONS work
2{isAdmin && <AdminPanel />} // logical
3{count > 0 ? count : 'None'} // ternary
4{items.map(i => <Item key={i.id} />)} // function call
5{(() => { return 'IIFE'; })()} // IIFE
6
7// STATEMENTS don't work
8{if (isAdmin) { return <Admin />; }} // ERROR!
9{for (let i of items) { ... }} // ERROR!
10{let x = 5;} // ERROR!

For complex logic, compute before the return:

jsx
1function Dashboard({ user, items }) {
2 // Do complex logic here
3 let content;
4 if (!user) {
5 content = <Login />;
6 } else if (items.length === 0) {
7 content = <EmptyState />;
8 } else {
9 content = <ItemList items={items} />;
10 }
11
12 // Return is clean
13 return <div className="dashboard">{content}</div>;
14}

Null, Undefined, and Booleans

These values render nothing in JSX:

jsx
1<div>
2 {null} {/* renders nothing */}
3 {undefined} {/* renders nothing */}
4 {true} {/* renders nothing */}
5 {false} {/* renders nothing */}
6</div>
7
8// Useful for conditional rendering
9<div>
10 {showBanner && <Banner />}
11 {user ?? <Login />}
12</div>

Watch out for falsy values that DO render:

jsx
1// These render as text!
2<div>{0}</div> {/* renders "0" */}
3<div>{''}</div> {/* renders nothing (empty string is special) */}
4<div>{NaN}</div> {/* renders "NaN" */}
5
6// Common bug with && operator
7{items.length && <List items={items} />} // Shows "0" if empty!
8
9// Fix: explicitly check boolean
10{items.length > 0 && <List items={items} />}

JSX Type Checking

TypeScript or PropTypes help catch JSX errors:

tsx
1// TypeScript
2interface ButtonProps {
3 variant: 'primary' | 'secondary';
4 children: React.ReactNode;
5 onClick?: () => void;
6}
7
8function Button({ variant, children, onClick }: ButtonProps) {
9 return (
10 <button className={`btn-${variant}`} onClick={onClick}>
11 {children}
12 </button>
13 );
14}
15
16// Usage - TypeScript catches errors
17<Button variant="tertiary">Save</Button> // Error: invalid variant
jsx
1// PropTypes (runtime checking)
2import PropTypes from 'prop-types';
3
4function Button({ variant, children, onClick }) {
5 return (
6 <button className={`btn-${variant}`} onClick={onClick}>
7 {children}
8 </button>
9 );
10}
11
12Button.propTypes = {
13 variant: PropTypes.oneOf(['primary', 'secondary']).isRequired,
14 children: PropTypes.node.isRequired,
15 onClick: PropTypes.func,
16};

JSX Best Practices

1. Extract Complex JSX

jsx
1// Before: hard to read
2function Dashboard() {
3 return (
4 <div>
5 {user ? (
6 <div className="user-panel">
7 <img src={user.avatar} alt={user.name} />
8 <h2>{user.name}</h2>
9 <p>{user.bio}</p>
10 <button onClick={logout}>Logout</button>
11 </div>
12 ) : (
13 <div className="login-panel">
14 <h2>Welcome</h2>
15 <button onClick={login}>Login</button>
16 </div>
17 )}
18 </div>
19 );
20}
21
22// After: extract components
23function UserPanel({ user, onLogout }) {
24 return (
25 <div className="user-panel">
26 <img src={user.avatar} alt={user.name} />
27 <h2>{user.name}</h2>
28 <p>{user.bio}</p>
29 <button onClick={onLogout}>Logout</button>
30 </div>
31 );
32}
33
34function LoginPanel({ onLogin }) {
35 return (
36 <div className="login-panel">
37 <h2>Welcome</h2>
38 <button onClick={onLogin}>Login</button>
39 </div>
40 );
41}
42
43function Dashboard() {
44 return (
45 <div>
46 {user ? (
47 <UserPanel user={user} onLogout={logout} />
48 ) : (
49 <LoginPanel onLogin={login} />
50 )}
51 </div>
52 );
53}

2. Use Semantic HTML

jsx
1// Avoid
2<div className="header">
3 <div className="nav">...</div>
4</div>
5
6// Prefer
7<header>
8 <nav>...</nav>
9</header>

3. Keep Returns Clean

jsx
1// Compute before return
2function ProductCard({ product }) {
3 const formattedPrice = new Intl.NumberFormat('en-US', {
4 style: 'currency',
5 currency: 'USD',
6 }).format(product.price);
7
8 const stockStatus = product.stock > 0 ? 'In Stock' : 'Out of Stock';
9 const stockClass = product.stock > 0 ? 'stock-good' : 'stock-bad';
10
11 return (
12 <article className="product-card">
13 <h2>{product.name}</h2>
14 <p className="price">{formattedPrice}</p>
15 <p className={stockClass}>{stockStatus}</p>
16 </article>
17 );
18}

Summary

Key JSX patterns:

  • Spread attributes: {...props}
  • Rest props: ({ known, ...rest })
  • Boolean attributes: disabled equals disabled={true}
  • Inline styles: camelCase objects
  • Only expressions in {}
  • null, undefined, booleans render nothing
  • Watch for 0 rendering with &&

Next, we'll put this knowledge into practice by building a profile card component!