Components and Props — Building Reusable UI Pieces

A React component is a function that accepts an object of inputs (props) and returns JSX describing what to render. Props are React’s mechanism for passing data from parent to child — they are the primary way components communicate. Understanding how to design component interfaces through props — what to expose, what to default, what to make required — is fundamental to building a reusable component library for the blog application.

Function Components and Props

// ── Basic component with props ─────────────────────────────────────────────────
function Greeting({ name, role }) {
    return (
        <p>Hello, {name}! You are logged in as {role}.</p>
    );
}

// Usage:
<Greeting name="Alice" role="editor" />

// ── Destructuring props in the parameter (recommended) ────────────────────────
function PostCard({ title, body, author, publishedAt, viewCount = 0 }) {
    //                                                   ↑ default value
    return (
        <article>
            <h2>{title}</h2>
            <p>{body}</p>
            <footer>
                {author.name} · {new Date(publishedAt).toLocaleDateString()} · {viewCount} views
            </footer>
        </article>
    );
}

// ── Using the whole props object (less common) ─────────────────────────────────
function PostCard(props) {
    const { title, body } = props;   // destructure inside the function
    return <article>...</article>;
}
Note: Props are read-only — a component must never modify its own props. This is one of React’s fundamental rules: data flows down (from parent to child via props), events flow up (from child to parent via callback props). If a child needs to update data, the parent passes a callback function as a prop (onLike={handleLike}), the child calls it, and the parent updates its state, causing a re-render that flows new props back down to the child.

Tip: The special children prop receives anything placed between a component’s opening and closing tags. <Card><h1>Title</h1></Card> gives the Card component a children prop equal to <h1>Title</h1>. Use children for layout and wrapper components (Card, Modal, Section) that need to render arbitrary content inside a styled container.
Warning: Component names must start with a capital letter — PostCard, not postCard. React uses the capitalisation to distinguish between HTML elements (<div>, <p>) and React components (<PostCard>). A lowercase component name is treated as an HTML element, which means <postCard /> would attempt to create a non-standard <postcard> DOM element rather than calling your component function.

Reusable Blog Components

// src/components/Avatar.jsx
function Avatar({ src, name, size = 40 }) {
    return (
        <img
            src={src || "/default-avatar.png"}
            alt={`${name}'s avatar`}
            width={size}
            height={size}
            className="rounded-full object-cover"
            style={{ width: size, height: size }}
        />
    );
}

// src/components/Badge.jsx
function Badge({ children, variant = "default" }) {
    const colours = {
        default:   "bg-gray-100 text-gray-700",
        success:   "bg-green-100 text-green-700",
        warning:   "bg-yellow-100 text-yellow-700",
        published: "bg-blue-100 text-blue-700",
        draft:     "bg-gray-100 text-gray-500",
    };
    return (
        <span className={`px-2 py-1 rounded text-xs font-medium ${colours[variant] || colours.default}`}>
            {children}
        </span>
    );
}

// src/components/Button.jsx
function Button({ children, onClick, variant = "primary", disabled = false, type = "button" }) {
    const styles = {
        primary:   "bg-blue-600 text-white hover:bg-blue-700",
        secondary: "bg-gray-100 text-gray-700 hover:bg-gray-200",
        danger:    "bg-red-600 text-white hover:bg-red-700",
        ghost:     "bg-transparent text-blue-600 hover:bg-blue-50",
    };
    return (
        <button
            type={type}
            onClick={onClick}
            disabled={disabled}
            className={`px-4 py-2 rounded font-medium transition-colors
                        ${styles[variant]}
                        ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`}
        >
            {children}
        </button>
    );
}

// Usage:
<Avatar src={user.avatar_url} name={user.name} size={48} />
<Badge variant="published">Published</Badge>
<Button variant="primary" onClick={handleSubmit}>Save Post</Button>
<Button variant="danger" disabled={isDeleting}>
    {isDeleting ? "Deleting..." : "Delete"}
</Button>

Children Prop for Layout Components

// Layout component using children
function Card({ title, children, className = "" }) {
    return (
        <div className={`bg-white rounded-lg shadow p-6 ${className}`}>
            {title && <h3 className="text-lg font-semibold mb-4">{title}</h3>}
            {children}
        </div>
    );
}

// Usage — children is everything between the tags:
<Card title="Recent Posts">
    <PostList posts={posts} />
    <Pagination page={page} total={total} />
</Card>

Common Mistakes

Mistake 1 — Lowercase component name

❌ Wrong — treated as HTML element, not component:

function postCard(props) { return <article>...</article>; }
<postCard title="Hello" />   // creates <postcard> DOM element, not a React component!

✅ Correct — always start with a capital letter:

function PostCard(props) { ... }
<PostCard title="Hello" />   // ✓ calls the PostCard function

Mistake 2 — Modifying props directly

❌ Wrong — props are read-only:

function PostCard({ post }) {
    post.title = post.title.toUpperCase();   // NEVER mutate props!
    return <h2>{post.title}</h2>;
}

✅ Correct — compute derived values without mutation:

function PostCard({ post }) {
    const upperTitle = post.title.toUpperCase();   // ✓ new variable
    return <h2>{upperTitle}</h2>;
}

Quick Reference

Pattern Code
Function component function Name({ prop1, prop2 }) { return <JSX />; }
Default prop value function Name({ size = 40 })
Children prop function Card({ children }) { return <div>{children}</div>; }
Pass callback <Button onClick={() => handleClick(id)} />
Spread props <input {...inputProps} />

🧠 Test Yourself

A LikeButton component needs to update the like count in its parent. Props are read-only, so the child cannot update the parent’s state directly. What is the React pattern for this?