Good Code
The good version gives callers a clear slot for actions and content, so each variant composes the panel it needs.
Lesson 10
Prefer flexible component slots over growing boolean prop combinations.
import type { ReactNode } from "react";
type PanelProps = {
title: string;
tone?: "neutral" | "danger";
actions?: ReactNode;
children: ReactNode;
};
export function Panel({
title,
tone = "neutral",
actions,
children,
}: PanelProps) {
// Slots let callers compose the variant-specific UI.
return (
<section data-tone={tone}>
<header>
<h2>{title}</h2>
{actions}
</header>
<div>{children}</div>
</section>
);
}
export function DeleteReviewPanel({ onDelete }: { onDelete: () => void }) {
return (
<Panel
title="Delete review"
tone="danger"
actions={<button type="button" onClick={onDelete}>Delete</button>}
>
<p>This review and its comments will be removed.</p>
</Panel>
);
}import type { ReactNode } from "react";
type PanelProps = {
title: string;
mode: "details" | "delete" | "approval";
showFooter: boolean;
showDeleteButton: boolean;
onDelete?: () => void;
children: ReactNode;
};
export function Panel({
title,
mode,
showFooter,
showDeleteButton,
onDelete,
children,
}: PanelProps) {
// Boolean flags create combinations the component must police.
return (
<section data-mode={mode}>
<h2>{title}</h2>
<div>{children}</div>
{showFooter ? <footer>Review actions</footer> : null}
{showDeleteButton ? (
<button type="button" onClick={onDelete}>Delete</button>
) : null}
</section>
);
}The good version gives callers a clear slot for actions and content, so each variant composes the panel it needs.
The bad version grows mode and boolean props for every new variation. Those combinations make the component harder to understand and easier to call incorrectly.