|$ curl https://forge-ai.dev/api/markdown?path=docs/react/components
$cat docs/react-components.md
updated Recently·40 min read·published

React Components

ReactComponentsBeginner to Advanced🎯Free Tools
Introduction

Components are the building blocks of every React application. A component is a reusable, self-contained piece of UI that accepts inputs (props) and returns React elements describing what should appear on screen. Modern React exclusively uses function components — class components are legacy.

Good component design is the foundation of maintainable React code. Well-designed components are small, focused, reusable, and composable. They have clear interfaces (props), manage minimal state, and follow the single responsibility principle.

Functional Components

A functional component is a JavaScript function that accepts a props object and returns JSX. React calls the function to determine what to render. When props or state change, React calls the function again.

functional-components.jsx
JSX
1// Simple function component
2function Greeting({ name }) {
3 return <h1>Hello, {name}!</h1>;
4}
5
6// Arrow function component (equivalent)
7const Greeting = ({ name }) => <h1>Hello, {name}!</h1>;
8
9// Component with logic
10function UserCard({ user, showEmail = false }) {
11 const initials = user.name
12 .split(" ")
13 .map(n => n[0])
14 .join("")
15 .toUpperCase();
16
17 const memberSince = new Date(user.createdAt).getFullYear();
18
19 return (
20 <div className="user-card">
21 <div className="avatar">{initials}</div>
22 <h3>{user.name}</h3>
23 {showEmail && <p className="text-gray-500">{user.email}</p>}
24 <p className="text-sm text-gray-400">Member since {memberSince}</p>
25 </div>
26 );
27}
28
29// Usage
30<UserCard user={{ name: "Jane Doe", email: "jane@example.com", createdAt: "2020-03-15" }} showEmail />

info

Always use named function declarations (not arrow functions) for components. Named functions show up in stack traces, making debugging much easier. Arrow functions show as "Anonymous" in error messages.
Props — Component Interfaces

Props (short for "properties") are how data flows from parent to child. Props are read-only — a component must never modify its own props. Destructure props in the function signature for clarity.

props-typescript.jsx
JSX
1// Props interface with TypeScript
2interface ButtonProps {
3 children: React.ReactNode;
4 variant?: "primary" | "secondary" | "danger";
5 size?: "sm" | "md" | "lg";
6 disabled?: boolean;
7 onClick?: () => void;
8 type?: "button" | "submit" | "reset";
9}
10
11function Button({
12 children,
13 variant = "primary",
14 size = "md",
15 disabled = false,
16 onClick,
17 type = "button",
18}: ButtonProps) {
19 const baseClasses = "font-mono rounded transition-colors";
20 const sizeClasses = {
21 sm: "px-2 py-1 text-xs",
22 md: "px-4 py-2 text-sm",
23 lg: "px-6 py-3 text-base",
24 };
25 const variantClasses = {
26 primary: "bg-blue-600 text-white hover:bg-blue-700",
27 secondary: "bg-gray-600 text-white hover:bg-gray-700",
28 danger: "bg-red-600 text-white hover:bg-red-700",
29 };
30
31 return (
32 <button
33 type={type}
34 disabled={disabled}
35 onClick={onClick}
36 className={`${baseClasses} ${sizeClasses[size]} ${variantClasses[variant]}
37 ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
38 >
39 {children}
40 </button>
41 );
42}
43
44// Usage
45<Button variant="primary" size="lg" onClick={handleSubmit}>
46 Submit Form
47</Button>
48<Button variant="danger" size="sm" disabled>
49 Delete
50</Button>

Children prop — the most important special prop:

children-prop.jsx
JSX
1// children — any JSX passed between opening and closing tags
2function Card({ title, children, footer }) {
3 return (
4 <div className="card">
5 <div className="card-header">
6 <h3>{title}</h3>
7 </div>
8 <div className="card-body">{children}</div>
9 {footer && <div className="card-footer">{footer}</div>}
10 </div>
11 );
12}
13
14// Usage — children goes between the tags
15<Card
16 title="User Profile"
17 footer={<button>Save Changes</button>}
18>
19 <p>Name: Jane Doe</p>
20 <p>Email: jane@example.com</p>
21 <Input label="Bio" multiline />
22</Card>
23
24// children can be anything — elements, text, functions
25<Card title="Dynamic Content">
26 {(isOpen) => isOpen ? <ExpandedView /> : <CollapsedView />}
27</Card>
28
29// Render prop pattern — children as a function
30function DataFetcher({ url, children }) {
31 const { data, loading, error } = useFetch(url);
32 return children({ data, loading, error });
33}
34
35// Usage
36<DataFetcher url="/api/users">
37 {({ data, loading, error }) => {
38 if (loading) return <Spinner />;
39 if (error) return <ErrorMessage error={error} />;
40 return <UserList users={data} />;
41 }}
42</DataFetcher>
📝

note

The childrenprop is React's mechanism for composition — it lets parent components wrap arbitrary content inside other components. This is how patterns like Layout, Card, Modal, and Provider work.
Composition Patterns

Composition is the core design principle in React. Instead of inheriting from base classes, you compose behavior by combining smaller components. This leads to more flexible, testable, and reusable code.

composition.jsx
JSX
1// Composition: small components combined into larger ones
2function PageLayout({ sidebar, children }) {
3 return (
4 <div className="flex min-h-screen">
5 <aside className="w-64 border-r">{sidebar}</aside>
6 <main className="flex-1 p-8">{children}</main>
7 </div>
8 );
9}
10
11function Dashboard({ user }) {
12 return (
13 <PageLayout
14 sidebar={
15 <nav>
16 <NavLink to="/dashboard">Overview</NavLink>
17 <NavLink to="/analytics">Analytics</NavLink>
18 <NavLink to="/settings">Settings</NavLink>
19 </nav>
20 }
21 >
22 <h1>Welcome back, {user.name}</h1>
23 <StatsGrid />
24 <RecentActivity />
25 </PageLayout>
26 );
27}
28
29// Slots pattern — named children
30function Dialog({ title, children, actions }) {
31 return (
32 <div className="dialog">
33 <div className="dialog-header">{title}</div>
34 <div className="dialog-body">{children}</div>
35 <div className="dialog-actions">{actions}</div>
36 </div>
37 );
38}
39
40// Usage with named slots
41<Dialog
42 title="Confirm Deletion"
43 actions={
44 <>
45 <Button onClick={onCancel}>Cancel</Button>
46 <Button variant="danger" onClick={onConfirm}>Delete</Button>
47 </>
48 }
49>
50 <p>Are you sure you want to delete this item? This cannot be undone.</p>
51</Dialog>
Fragments

Fragments let you group multiple elements without adding an extra DOM node. They are essential when a component needs to return multiple elements without wrapping them in a div.

fragments.jsx
JSX
1import { Fragment } from "react";
2
3// Without fragment — adds unnecessary div
4function BadTableHeader() {
5 return (
6 <div> {/* This div breaks table semantics! */}
7 <th>Name</th>
8 <th>Email</th>
9 <th>Role</th>
10 </div>
11 );
12}
13
14// With Fragment — no extra DOM node
15function TableHeader() {
16 return (
17 <>
18 <th>Name</th>
19 <th>Email</th>
20 <th>Role</th>
21 </>
22 );
23}
24
25// Long-form Fragment when you need a key
26function DescriptionList({ items }) {
27 return (
28 <dl>
29 {items.map(item => (
30 <Fragment key={item.id}>
31 <dt>{item.label}</dt>
32 <dd>{item.value}</dd>
33 </Fragment>
34 ))}
35 </dl>
36 );
37}
38
39// Fragment for returning multiple elements from a component
40function SiteMetadata() {
41 return (
42 <>
43 <meta charSet="utf-8" />
44 <meta name="viewport" content="width=device-width, initial-scale=1" />
45 <title>My App</title>
46 <meta name="description" content="App description" />
47 </>
48 );
49}

info

The shorthand <>...</> syntax is preferred for most cases. Use the long-form <Fragment key={...}> only when you need to pass a key (usually in maps).
Conditional Rendering

React supports several patterns for conditionally rendering UI elements. Choose the pattern based on readability and complexity.

conditional-rendering.jsx
JSX
1function UserStatus({ user, isLoggedIn, notifications }) {
2 // Pattern 1: if/else with early returns
3 if (!user) {
4 return <LoginForm />;
5 }
6
7 // Pattern 2: Ternary operator (inline)
8 return (
9 <div>
10 <h1>{isLoggedIn ? `Welcome, ${user.name}` : "Welcome, Guest"}</h1>
11
12 {/* Pattern 3: Logical AND (&&) */}
13 {notifications.length > 0 && (
14 <div className="badge">{notifications.length} new</div>
15 )}
16
17 {/* Pattern 4: Variable assignment */}
18 {(() => {
19 switch (user.role) {
20 case "admin":
21 return <AdminPanel />;
22 case "editor":
23 return <EditorPanel />;
24 default:
25 return <ViewerPanel />;
26 }
27 })()}
28
29 {/* Pattern 5: Lookup table */}
30 {({
31 admin: <AdminDashboard />,
32 editor: <EditorDashboard />,
33 viewer: <ViewerDashboard />,
34 })[user.role]}
35
36 {/* Pattern 6: Nullish coalescing for fallback */}
37 {user.avatar ?? <DefaultAvatar />}
38 </div>
39 );
40}
Lists & Keys

Rendering lists in React uses Array.map(). Each list item needs a stable, unique key prop so React can efficiently update the DOM when items are added, removed, or reordered.

lists-keys.jsx
JSX
1function TodoList({ todos, onToggle, onDelete }) {
2 return (
3 <ul>
4 {todos.map(todo => (
5 <li key={todo.id} className={todo.done ? "line-through" : ""}>
6 <input
7 type="checkbox"
8 checked={todo.done}
9 onChange={() => onToggle(todo.id)}
10 />
11 <span>{todo.text}</span>
12 <button onClick={() => onDelete(todo.id)}>×</button>
13 </li>
14 ))}
15 </ul>
16 );
17}
18
19// Key rules:
20// ✅ Use unique IDs (database IDs, UUIDs)
21// key={user.id}
22
23// ✅ Use stable identifiers
24// key={`item-${index}`} // only if list is static
25
26// ❌ Never use array index for dynamic lists
27// key={index} // causes bugs when items reorder
28
29// Grouped lists with keys
30function GroupedUsers({ groups }) {
31 return (
32 <div>
33 {groups.map(group => (
34 <div key={group.id}>
35 <h3>{group.name}</h3>
36 {group.users.map(user => (
37 <div key={user.id}>{user.name}</div>
38 ))}
39 </div>
40 ))}
41 </div>
42 );
43}

warning

Never use array indices as keys for lists that can be reordered, filtered, or have items added/removed. React uses keys to identify which items changed — wrong keys cause incorrect DOM updates, lost focus state, and animation glitches.
Render Props

The render prop pattern passes a function as a prop (or children) that the component calls to render its content. It gives parent components control over what is rendered while the child manages behavior.

render-props.jsx
JSX
1// Render prop component — manages mouse tracking logic
2function MouseTracker({ render }) {
3 const [position, setPosition] = useState({ x: 0, y: 0 });
4
5 useEffect(() => {
6 const handleMouseMove = (e) => {
7 setPosition({ x: e.clientX, y: e.clientY });
8 };
9 window.addEventListener("mousemove", handleMouseMove);
10 return () => window.removeEventListener("mousemove", handleMouseMove);
11 }, []);
12
13 return render(position);
14}
15
16// Usage — parent decides how to display the mouse position
17function App() {
18 return (
19 <div>
20 <MouseTracker
21 render={({ x, y }) => (
22 <p>Mouse is at ({x}, {y})</p>
23 )}
24 />
25 <MouseTracker
26 render={({ x, y }) => (
27 <Crosshair x={x} y={y} />
28 )}
29 />
30 </div>
31 );
32}
33
34// Toggle component with render prop
35function Toggle({ initial = false, children }) {
36 const [on, setOn] = useState(initial);
37 const toggle = () => setOn(prev => !prev);
38
39 return children({ on, toggle });
40}
41
42// Usage
43<Toggle initial={true}>
44 {({ on, toggle }) => (
45 <div>
46 <button onClick={toggle}>{on ? "ON" : "OFF"}</button>
47 {on && <p>Content is visible!</p>}
48 </div>
49 )}
50</Toggle>
Component Design Principles

Well-designed components follow these principles. Following them leads to code that is easier to test, reuse, and maintain.

design-principles.jsx
JSX
1// 1. Single Responsibility — one reason to change
2// ❌ Bad: component does too many things
3function UserProfile({ userId }) {
4 const [user, setUser] = useState(null);
5 const [posts, setPosts] = useState([]);
6 const [editing, setEditing] = useState(false);
7 const [formData, setFormData] = useState({});
8 // ... fetch, validate, save, delete, etc.
9}
10
11// ✅ Good: split into focused components
12function UserProfile({ userId }) {
13 return (
14 <UserDataFetcher userId={userId}>
15 {({ user }) => (
16 <UserDetails user={user} />
17 )}
18 </UserDataFetcher>
19 );
20}
21
22// 2. Favor composition over configuration
23// ❌ Bad: too many props to configure behavior
24<Card
25 variant="outlined"
26 headerVisible={true}
27 footerVisible={true}
28 hoverable={true}
29 clickable={true}
30 avatarPosition="left"
31 badgeColor="green"
32/>
33
34// ✅ Good: compose instead
35<Card>
36 <CardHeader>
37 <Avatar user={user} />
38 <Badge color="green">Active</Badge>
39 </CardHeader>
40 <CardBody>{content}</CardBody>
41 <CardFooter>
42 <Button>Action</Button>
43 </CardFooter>
44</Card>
45
46// 3. Keep components pure — no side effects in render
47// ❌ Bad: side effect during render
48function BadComponent({ items }) {
49 document.title = `${items.length} items`; // side effect!
50 return <ul>{items.map(...)}</ul>;
51}
52
53// ✅ Good: use useEffect for side effects
54function GoodComponent({ items }) {
55 useEffect(() => {
56 document.title = `${items.length} items`;
57 }, [items.length]);
58 return <ul>{items.map(...)}</ul>;
59}

best practice

Keep components small (under 100 lines when possible). If a component file grows beyond 200 lines, it's a signal to extract sub-components or custom hooks. Small components are easier to test, understand, and reuse.
Error Boundaries

Error boundaries catch JavaScript errors anywhere in their child component tree during rendering, lifecycle methods, and constructors. They display a fallback UI instead of crashing the entire app.

error-boundary.jsx
JSX
1import { Component } from "react";
2
3class ErrorBoundary extends Component {
4 constructor(props) {
5 super(props);
6 this.state = { hasError: false, error: null };
7 }
8
9 static getDerivedStateFromError(error) {
10 return { hasError: true, error };
11 }
12
13 componentDidCatch(error, errorInfo) {
14 console.error("Error caught by boundary:", error, errorInfo);
15 // Report to error tracking service
16 reportError(error, errorInfo);
17 }
18
19 render() {
20 if (this.state.hasError) {
21 return (
22 <div className="error-fallback">
23 <h2>Something went wrong</h2>
24 <p>{this.state.error?.message}</p>
25 <button onClick={() => this.setState({ hasError: false })}>
26 Try Again
27 </button>
28 </div>
29 );
30 }
31 return this.props.children;
32 }
33}
34
35// Usage — wrap components that might fail
36function App() {
37 return (
38 <div>
39 <Header />
40 <ErrorBoundary>
41 <Dashboard /> {/* If this crashes, the rest of the app survives */}
42 </ErrorBoundary>
43 <Footer />
44 </div>
45 );
46}
📝

note

Error boundaries cannot catch errors in event handlers, async code (setTimeout, promises), server-side rendering, or errors thrown in the boundary itself. For event handler errors, use try/catch blocks.