JSX and Components
What is JSX?
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code in your JavaScript files. It's not HTML, but a syntactic sugar that gets compiled to regular JavaScript.
JSX Compilation
// JSX
const element = <h1 className='greeting'>Hello, World!</h1>;
// Compiles to (React 17+)
import { jsx as _jsx } from 'react/jsx-runtime';
const element = _jsx('h1', {
className: 'greeting',
children: 'Hello, World!',
});
// Before React 17
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, World!'
);
JSX Rules
- Single Root Element: Components must return a single root element
// ❌ Wrong
function App() {
return (
<h1>Title</h1>
<p>Paragraph</p>
);
}
// ✅ Correct - Using Fragment
function App() {
return (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
}
- className instead of class
<div className='container'>Content</div>
- camelCase for attributes
<button onClick={handleClick} onMouseEnter={handleHover}>
Click me
</button>
- Self-closing tags must have /
<img src="image.jpg" alt="Description" />
<input type="text" />
Functional Components
Modern React uses functional components as the primary way to create components.
Basic Component
function Welcome() {
return <h1>Hello, React!</h1>;
}
// Arrow function syntax
const Welcome = () => {
return <h1>Hello, React!</h1>;
};
// Implicit return (single expression)
const Welcome = () => <h1>Hello, React!</h1>;
Component with Props
function Welcome({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old</p>
</div>
);
}
// Usage
<Welcome name='Alice' age={25} />;
TypeScript Components
interface WelcomeProps {
name: string;
age: number;
isActive?: boolean; // Optional prop
}
const Welcome = ({ name, age, isActive = true }: WelcomeProps) => {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
{isActive && <span>Active User</span>}
</div>
);
};
// Or using React.FC (less common in modern React)
const Welcome: React.FC<WelcomeProps> = ({ name, age }) => {
return <h1>Hello, {name}!</h1>;
};
JavaScript Expressions in JSX
You can embed any JavaScript expression in JSX using curly braces {}.
const name = 'Alice';
const age = 25;
function Greeting() {
const formatName = name => name.toUpperCase();
return (
<div>
{/* Variables */}
<h1>Hello, {name}!</h1>
{/* Expressions */}
<p>Next year you'll be {age + 1}</p>
{/* Function calls */}
<p>Uppercase: {formatName(name)}</p>
{/* Ternary operators */}
<p>{age >= 18 ? 'Adult' : 'Minor'}</p>
{/* Template literals */}
<p>{`User ${name} is ${age} years old`}</p>
</div>
);
}
Component Composition
Breaking down UI into smaller, reusable components.
// Small, focused components
const Avatar = ({ src, alt, size = 50 }) => (
<img
src={src}
alt={alt}
style={{ width: size, height: size, borderRadius: '50%' }}
/>
);
const UserInfo = ({ name, email }) => (
<div>
<h3>{name}</h3>
<p>{email}</p>
</div>
);
// Composed component
const UserCard = ({ user }) => (
<div className='user-card'>
<Avatar src={user.avatar} alt={user.name} />
<UserInfo name={user.name} email={user.email} />
</div>
);
// Usage
const user = {
name: 'Alice Johnson',
email: 'alice@example.com',
avatar: 'https://example.com/avatar.jpg',
};
<UserCard user={user} />;
Children Prop
Components can receive children elements.
const Card = ({ children, title }) => (
<div className='card'>
{title && <h2>{title}</h2>}
<div className='card-content'>{children}</div>
</div>
);
// Usage
<Card title='User Profile'>
<p>This is the card content</p>
<button>Click me</button>
</Card>;
TypeScript with Children
interface CardProps {
title?: string;
children: React.ReactNode;
}
const Card = ({ title, children }: CardProps) => (
<div className='card'>
{title && <h2>{title}</h2>}
{children}
</div>
);
Modern Component Patterns
Destructuring Props with Defaults
const Button = ({
text = 'Click me',
variant = 'primary',
disabled = false,
onClick,
}) => (
<button
className={`btn btn-${variant}`}
disabled={disabled}
onClick={onClick}
>
{text}
</button>
);
Spread Props
const Input = ({ label, ...inputProps }) => (
<div className='form-group'>
<label>{label}</label>
<input {...inputProps} />
</div>
);
// Usage
<Input label='Email' type='email' placeholder='Enter email' required />;
Conditional Rendering Patterns
const UserStatus = ({ user }) => {
// Early return pattern
if (!user) {
return <p>Loading...</p>;
}
return (
<div>
{/* Logical AND */}
{user.isPremium && <span className='badge'>Premium</span>}
{/* Ternary operator */}
{user.isOnline ? (
<span className='status online'>Online</span>
) : (
<span className='status offline'>Offline</span>
)}
{/* Nullish coalescing */}
<p>Points: {user.points ?? 0}</p>
</div>
);
};
Component File Organization
Modern Project Structure
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.module.css
│ │ └── index.ts
│ ├── Card/
│ │ ├── Card.tsx
│ │ ├── Card.module.css
│ │ └── index.ts
│ └── index.ts // Barrel export
├── App.tsx
└── main.tsx
Barrel Exports
// components/index.ts
export { Button } from './Button';
export { Card } from './Card';
export { UserCard } from './UserCard';
// Usage in other files
import { Button, Card, UserCard } from '@/components';
Best Practices
- One Component Per File - Keep components focused and maintainable
- Use Descriptive Names -
UserProfileCardinstead ofCard1 - Keep Components Small - Single Responsibility Principle
- Use TypeScript - Better type safety and developer experience
- Destructure Props - Cleaner and more readable
- Avoid Inline Functions in JSX - Can cause unnecessary re-renders
- Use Fragments - Avoid unnecessary wrapper divs
Common Pitfalls
1. Mutating Props
// ❌ Never mutate props
function Component({ user }) {
user.name = 'New Name'; // Don't do this!
return <div>{user.name}</div>;
}
// ✅ Props are read-only
function Component({ user }) {
return <div>{user.name}</div>;
}
2. Missing Keys in Lists
// ❌ Missing key
users.map(user => <UserCard user={user} />);
// ✅ With key
users.map(user => <UserCard key={user.id} user={user} />);
3. Incorrect Event Handlers
// ❌ Immediately invoked
<button onClick={handleClick()}>Click</button>
// ✅ Pass function reference
<button onClick={handleClick}>Click</button>
// ✅ With parameters
<button onClick={() => handleClick(id)}>Click</button>
Practice Exercise
Create a ProductCard component that:
- Displays product image, name, price, and rating
- Shows a "Sale" badge if on sale
- Has an "Add to Cart" button
- Uses TypeScript for type safety
- Follows all best practices
interface Product {
id: string;
name: string;
price: number;
image: string;
rating: number;
onSale?: boolean;
}
interface ProductCardProps {
product: Product;
onAddToCart: (id: string) => void;
}
// Implement the component here
Next Steps
- Learn about Props and Composition
- Understand Virtual DOM