React Tutorial 0/120 lessons ~6 min read Lesson 30

    Real UI Components

    Real UI components in React: reusable Button, Card, Badge, Alert, Input, Modal, and Navbar with clear APIs, prop forwarding, composition, and accessibility.

    Course progress0%
    Focus
    36 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Real UI Components

    Real UI Components are reusable React components designed to represent actual interface elements found in modern applications.

    So far, we have learned how to create components, pass props, handle events, render lists, conditionally render UI, and style with CSS, CSS Modules, and Tailwind. Now we combine those concepts.

    Instead of isolated examples, build real application components:

    text
    Navbar Button Card Modal Alert Badge
    Input Dropdown Sidebar Table Pagination

    The goal is to create reusable components with a clear API, predictable behavior, and consistent styling.

    Learning Objectives

    1. What real UI components are and why reuse matters
    2. Design clear component APIs (variants, sizes, children)
    3. Prop forwarding and native HTML attributes
    4. Build Button, Card, Badge, Alert, Input, Modal, Navbar, Spinner
    5. UI vs page vs feature components
    6. Accessibility, semantic HTML, and composition

    What Is a Real UI Component?

    A real UI component represents a meaningful piece of an application's interface.

    tsx
    <Button>Save</Button>
    <Card
    title="React Fundamentals"
    description="Learn React step by step."
    />

    A real UI component usually combines:

    text
    Structure + Styling + Behavior + Props + Accessibility + Reusability

    Why Build Reusable UI Components?

    Without reusable components, 30 buttons across pages drift apart — different padding, colors, radius, hover, and disabled behavior. Centralize instead:

    tsx
    <Button variant="primary">
    Save
    </Button>
    text
    30 usages → Button → Consistent UI

    Component API

    tsx
    <Button
    variant="primary"
    size="large"
    disabled={false}
    >
    Save
    </Button>

    Props like variant, size, disabled, and children define how consumers interact. A good API is predictable, easy to understand, flexible, and difficult to misuse.

    Building a Real Button

    tsx
    function Button({
    children,
    variant = "primary",
    size = "medium",
    disabled = false
    }) {
    const variants = {
    primary: "bg-blue-600 text-white hover:bg-blue-700",
    secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
    danger: "bg-red-600 text-white hover:bg-red-700"
    };
    const sizes = {
    small: "px-3 py-1.5 text-sm",
    medium: "px-4 py-2",
    large: "px-6 py-3 text-lg"
    };
    const className = [
    "rounded-md font-medium transition",
    variants[variant],
    sizes[size],
    disabled && "opacity-50 cursor-not-allowed"
    ]
    .filter(Boolean)
    .join(" ");
    return (
    <button className={className} disabled={disabled}>
    {children}
    </button>
    );
    }
    <Button>Save</Button>
    <Button variant="secondary">Cancel</Button>
    <Button variant="danger">Delete</Button>
    <Button size="large">Continue</Button>

    Understanding children

    children is the content between opening and closing tags:

    tsx
    function Button({ children }) {
    return <button>{children}</button>;
    }
    <Button>Save</Button>
    // children = "Save"

    Accepting Native HTML Props

    Collect remaining props with the rest operator:

    tsx
    function Button({
    children,
    variant = "primary",
    size = "medium",
    ...props
    }) {
    return (
    <button className="..." {...props}>
    {children}
    </button>
    );
    }
    <Button
    type="submit"
    aria-label="Save changes"
    disabled={isSaving}
    >
    Save
    </Button>

    Why Prop Forwarding Matters

    text
    Consumer
    ↓
    <Button onClick={...} disabled={...}>
    ↓
    <button onClick={...} disabled={...}>

    This pattern is called prop forwarding — appropriate native props reach the underlying element.

    Building a Card Component

    tsx
    function Card({ title, description, children }) {
    return (
    <article className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
    <h2 className="text-xl font-semibold text-gray-900">
    {title}
    </h2>
    <p className="mt-2 text-gray-600">{description}</p>
    <div className="mt-4">{children}</div>
    </article>
    );
    }
    <Card
    title="React Fundamentals"
    description="Learn the fundamentals of React."
    >
    <Button>Start Learning</Button>
    </Card>

    Composition: the card does not need to know what its child content is.

    Component Composition

    Prefer composing small components over giant ones like CourseCardWithButtonAndBadgeAndImage:

    tsx
    <Card>
    <Badge>Beginner</Badge>
    <h2>React Fundamentals</h2>
    <p>Learn React step by step.</p>
    <Button>Start Learning</Button>
    </Card>

    Building a Badge

    tsx
    function Badge({ children, variant = "default" }) {
    const variants = {
    default: "bg-gray-100 text-gray-800",
    success: "bg-green-100 text-green-800",
    warning: "bg-yellow-100 text-yellow-800",
    error: "bg-red-100 text-red-800"
    };
    return (
    <span className={`rounded-full px-3 py-1 text-sm ${variants[variant]}`}>
    {children}
    </span>
    );
    }
    <Badge>Draft</Badge>
    <Badge variant="success">Published</Badge>
    <Badge variant="warning">Pending</Badge>
    <Badge variant="error">Failed</Badge>

    Building an Alert

    tsx
    function Alert({ type = "info", title, children }) {
    const styles = {
    info: "bg-blue-50 text-blue-800",
    success: "bg-green-50 text-green-800",
    warning: "bg-yellow-50 text-yellow-800",
    error: "bg-red-50 text-red-800"
    };
    return (
    <div role="alert" className={`rounded-lg p-4 ${styles[type]}`}>
    {title && <h3 className="font-semibold">{title}</h3>}
    <div className="mt-1">{children}</div>
    </div>
    );
    }
    <Alert type="success" title="Success">
    Your profile has been updated.
    </Alert>

    role="alert" gives semantic meaning for assistive technologies.

    Building an Input Component

    tsx
    function Input({ label, error, ...props }) {
    return (
    <div>
    {label && (
    <label className="mb-1 block font-medium">{label}</label>
    )}
    <input
    className="w-full rounded-md border border-gray-300 px-3 py-2 focus:border-blue-500 focus:ring-2"
    {...props}
    />
    {error && (
    <p className="mt-1 text-sm text-red-600">{error}</p>
    )}
    </div>
    );
    }
    <Input label="Email" type="email" placeholder="Enter your email" />
    <Input label="Email" type="email" error="Please enter a valid email address." />
    tsx
    function Modal({ open, title, children, onClose }) {
    if (!open) return null;
    return (
    <div className="fixed inset-0 flex items-center justify-center bg-black/50">
    <div className="w-full max-w-md rounded-xl bg-white p-6">
    <div className="flex items-center justify-between">
    <h2 className="text-xl font-semibold">{title}</h2>
    <button onClick={onClose} aria-label="Close dialog">
    ×
    </button>
    </div>
    <div className="mt-4">{children}</div>
    </div>
    </div>
    );
    }
    <Modal
    open={isOpen}
    title="Delete Account"
    onClose={() => setIsOpen(false)}
    >
    <p>Are you sure you want to delete your account?</p>
    <Button variant="danger">Delete</Button>
    </Modal>

    Combines state, props, conditional rendering, events, children, and styling. Production modals also need dialog semantics, focus management, and keyboard handling.

    tsx
    function Navbar() {
    return (
    <nav className="border-b bg-white">
    <div className="mx-auto flex max-w-7xl items-center justify-between px-6 py-4">
    <a href="/" className="text-xl font-bold">
    MyApp
    </a>
    <div className="flex gap-6">
    <a href="/">Home</a>
    <a href="/courses">Courses</a>
    <a href="/about">About</a>
    </div>
    </div>
    </nav>
    );
    }

    Later enhance with responsive nav, mobile menu, active links, auth state, and dropdowns — without changing every page that uses it.

    Building a Loading Spinner

    tsx
    function Spinner() {
    return (
    <div
    className="h-6 w-6 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600"
    aria-label="Loading"
    role="status"
    />
    );
    }
    {loading && <Spinner />}

    Component Variants

    Prefer one component with a variant prop over PrimaryButton / DangerButton:

    tsx
    <Button variant="primary">Save</Button>
    <Button variant="danger">Delete</Button>

    Component Sizes

    tsx
    <Button size="small">Small</Button>
    <Button size="medium">Medium</Button>
    <Button size="large">Large</Button>
    const sizes = {
    small: "...",
    medium: "...",
    large: "..."
    };

    Boolean Props

    tsx
    <Button disabled={isSaving}>Save</Button>
    <Modal open={isOpen}>...</Modal>
    <Card featured>...</Card>

    Useful for simple on/off features.

    Avoid Too Many Props

    Avoid APIs like:

    tsx
    <Button blue rounded large shadow bold uppercase border ...>

    Prefer meaningful variants that communicate intent:

    tsx
    <Button variant="primary" size="large">
    Save
    </Button>

    Component States

    text
    Button: Normal, Hover, Focus, Active, Disabled, Loading
    Input: Empty, Focused, Filled, Invalid, Disabled, Read-only
    tsx
    <Button disabled={loading}>
    {loading ? "Saving..." : "Save"}
    </Button>

    Real UI Component Architecture

    text
    src/components/
    ├── Button/Button.jsx
    ├── Card/Card.jsx
    ├── Badge/Badge.jsx
    ├── Input/Input.jsx
    ├── Modal/Modal.jsx
    ├── Alert/Alert.jsx
    └── Navbar/Navbar.jsx

    Larger components can group tests and styles (or just JSX + tests when using Tailwind).

    UI Components vs Page Components

    text
    Application → Pages → Feature Components → UI Components
    CourseDetailsPage
    ├── CourseHeader → Badge, Button
    ├── CourseDescription
    └── LessonList → LessonItem

    Button is reusable across pages; CourseDetailsPage is a whole screen.

    UI Components vs Feature Components

    UI Components

    Generic: Button, Input, Modal, Badge, Card, Spinner

    Feature Components

    Domain-specific: CourseCard, CourseProgress, CheckoutForm, UserProfile, OrderSummary

    Composition Over Giant Components

    tsx
    function Dashboard() {
    return (
    <>
    <Navbar />
    <div className="flex">
    <Sidebar />
    <main>
    <StatsCard />
    <ActivityList />
    <RecentOrders />
    </main>
    </div>
    </>
    );
    }

    Accessibility Is Part of the Component

    tsx
    <button aria-label="Close" onClick={onClose}>×</button>
    <label htmlFor="email">Email</label>
    <input id="email" type="email" />
    <img src="/avatar.jpg" alt="John's profile" />

    Build accessibility into reusable components so every usage inherits it.

    Semantic HTML

    Use button for actions, a for navigation, nav, main, article appropriately. Avoid <div onClick> when a semantic element already exists.

    Real UI Component Mental Model

    text
    Component
    │
    ┌─────────────┼─────────────┐
    ▼ ▼ ▼
    API UI Behavior
    (props) (styles) (events)
    │ │ │
    └─────────────┼─────────────┘
    ▼
    Reusable Component

    Ask: What does it render? Which props? Which states? Which events? Is it accessible? Reusable? Is the API clear?

    A Complete Example

    tsx
    function Badge({ children }) {
    return (
    <span className="rounded-full bg-blue-100 px-3 py-1 text-sm text-blue-700">
    {children}
    </span>
    );
    }
    function Button({ children, ...props }) {
    return (
    <button
    className="rounded-md bg-blue-600 px-4 py-2 font-medium text-white hover:bg-blue-700"
    {...props}
    >
    {children}
    </button>
    );
    }
    function CourseCard({ title, description, level, onStart }) {
    return (
    <article className="rounded-xl border bg-white p-6 shadow-sm">
    <Badge>{level}</Badge>
    <h2 className="mt-3 text-xl font-bold">{title}</h2>
    <p className="mt-2 text-gray-600">{description}</p>
    <Button className="mt-5" onClick={onStart}>
    Start Learning
    </Button>
    </article>
    );
    }
    <CourseCard
    title="React Fundamentals"
    description="Learn React from the ground up."
    level="Beginner"
    onStart={() => console.log("Course started")}
    />
    text
    CourseCard → Badge + Heading + Description + Button

    Common Mistakes

    1. Too large — split unrelated sections
    2. Every div a component — extract when there is real responsibility or reuse
    3. Too early — extract when a pattern emerges
    4. Too many props — prefer composition or splitting
    5. Hiding native HTML — forward type, disabled, onClick, aria-*
    6. Ignoring accessibility — keyboard and assistive tech matter

    Best Practices

    1. Keep components focused
    2. Design clear APIs (variant, size)
    3. Support composition with children
    4. Forward appropriate native props
    5. Handle states: loading, disabled, error, empty, active, hover, focus
    6. Build accessibility into components
    7. Prefer composition over giant components
    8. Keep styling strategy consistent

    Practice Exercises

    1. Button — primary/secondary/danger + small/medium/large
    2. Card — title, description, children
    3. Alert — info/success/warning/error + accessible
    4. Input — label, error, placeholder, type, value, onChange
    5. Modal — open, title, children, onClose, overlay, a11y
    6. Course Dashboard — compose Navbar, Sidebar, Card, Badge, Button

    Interview Questions

    1. What is a reusable UI component?

    A React component that encapsulates meaningful UI, styling, and often behavior for reuse.

    2. What is component composition?

    Building larger interfaces by combining smaller components.

    3. What is the children prop?

    Content between a component's opening and closing tags.

    4. What is prop forwarding?

    tsx
    <button {...props}>

    5. What are component variants?

    tsx
    <Button variant="danger" />

    6. When should something become a component?

    When it has meaningful responsibility, reusable behavior, repeated structure, or improves organization.

    7. UI vs feature component?

    UI is generic (Button, Input); feature is domain-specific (CourseCard, CheckoutForm).

    8. Why accessibility in reusable components?

    Problems otherwise repeat everywhere the component is used.

    9. Why not every div?

    Excessive abstraction hurts readability — components should be meaningful units.

    10. What is a good component API?

    Predictable, focused, easy to use, flexible for real cases, hard to misuse.

    Quick Cheat Sheet

    ConceptPattern
    Children<Card>Content</Card>
    Native props...props on underlying element
    Variantsvariant="danger"
    Sizessize="large"
    CompositionCard + Badge + Button
    Conditional{loading && <Spinner />}
    A11yaria-label, labels, roles

    Key Takeaway

    Real UI components are where React becomes real application development — think in building blocks (Button, Card, Modal) not only HTML tags.

    text
    Props → Component → UI + State + Events → Reusable UI

    Create the right components: clear responsibilities, useful APIs, reusable behavior, consistent styling, and accessibility built in. Next: component libraries, design systems, forms, navigation, and application-level UI architecture.

    Ready to mark this lesson complete?Track your journey across the entire course.