If you've ever updated a button style on one page and then spent an hour hunting down every other page where that button appears, you already understand why component reusability matters. The pain you felt is the pain of a page-based approach meeting reality.
System intelligence demands that every repeated visual pattern be extracted into a reusable component — a single source of truth that renders consistently everywhere it's used.
What components actually are
A component is a self-contained piece of UI with defined inputs (props), consistent styling, and predictable behaviour. It's not a template — it's more like a LEGO brick. You don't rebuild the brick each time you use it; you connect it to other bricks in different configurations.
In practice, for a static or light-dynamic site, components are typically:
- HTML templates with placeholder slots (in Astro, Eleventy, or Hugo partials)
- CSS classes defined once and applied consistently
- Design tokens (colors, spacing, typography) referenced from a central variable file
You don't need React, Vue, or a component library framework to have components. You need discipline.
The component audit
Before building anything, audit every planned page and catalogue every repeated visual pattern. For a typical South African business site, here's what you'll find:
That's 8 components handling 35–60+ instances across a 10–15 page site. Without components, that's 35–60 separate HTML blocks to build and maintain. With components, it's 8 templates you build once.
Core component library
Here's the minimal component library I use for every project, with the key design decisions for each:
Section Header
Accepts: label (mono uppercase), heading (H2), description (paragraph), optional alignment prop (left/center). Every section on every page uses this instead of ad-hoc heading markup. This alone enforces visual consistency.
Service Card
Accepts: icon, title, description, link URL, link text. Used on the services hub page, in homepage service previews, and in sidebar "related services" blocks. One component, three contexts.
Testimonial Block
Accepts: quote text, author name, author title, company, optional photo URL. Same component renders identically whether it's in a testimonials page grid, a sidebar, or a homepage highlight section.
CTA Section
Accepts: headline, description, button text, button URL, variant (primary/secondary). The "primary" variant uses the gradient background. The "secondary" variant uses a bordered style. Same logic, different visual weight.
Image + Text Row
Accepts: image URL, image alt, heading, body text, optional bullet list, image position (left/right), optional link. This is the most-used component on most sites — the classic two-column layout that appears on service pages, about sections, and blog posts.
Before vs. after
Without components
- Service card HTML copy-pasted 6 times
- Each copy has slightly different padding/margin
- Changing the card style means editing 6 files
- Blog has a completely different card style
- New service page takes 4 hours to build
- Design drift accumulates over months
With components
- Service card defined once as a partial
- All instances render identically
- Changing the card style means editing 1 file
- Blog uses the same card with different props
- New service page takes 45 minutes to build
- Design consistency is automatic
Props, not copies
The key distinction: a component is not a copy-paste template. It accepts props — data that customises its output without changing its structure.
In a static site generator like Astro, this looks like:
<!-- Component definition: ServiceCard.astro -->
<div class="service-card">
<i class="fas fa-{icon}" aria-hidden="true"></i>
<h3>{title}</h3>
<p>{description}</p>
<a href="{link}">{linkText}</a>
</div>
<!-- Usage on services page -->
<ServiceCard
icon="laptop-code"
title="Web Design"
description="Custom websites built as systems, not pages."
link="/services/web-design"
linkText="Learn more"
/>
<!-- Usage on homepage with different data -->
<ServiceCard
icon="search"
title="SEO"
description="Rank on Google without paying for ads forever."
link="/services/seo"
linkText="See our SEO approach"
/>
Same component. Different props. Identical visual output. Zero duplication.
Design tokens, not magic numbers
Components are only as consistent as the design values they reference. If one component uses
padding: 1.5rem and another uses padding: 24px, they're technically the same
but practically fragile.
Design tokens solve this by defining every visual value once:
:root {
/* Spacing scale */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
--space-3xl: 4rem;
/* Typography scale */
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--text-4xl: 2.25rem;
/* Border radius */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 2px 8px rgba(0,0,0,.15);
--shadow-md: 0 8px 24px rgba(0,0,0,.2);
--shadow-lg: 0 16px 48px rgba(0,0,0,.3);
}
Every component references tokens, never raw values. If you decide all cards should have slightly more
rounded corners, you change --radius-md in one place and every card on the site updates.
When component thinking fails
Component reusability isn't always the answer. There are three failure modes:
- Over-abstracting: Creating a component for something used only once. If a pattern appears once, it's not a component — it's just HTML.
- Prop explosion: If your component has 15 props and 6 conditional variants, it's not a component — it's a configuration file that happens to render HTML. Split it into smaller components.
- Ignoring context: A testimonial card that works on a testimonials page might not work in a homepage hero section. Forcing the same component into every context produces mediocrity, not consistency.
The rule: extract a component when a pattern appears three or more times, and design its props to cover the actual variations you need — not hypothetical future variations.
Scaling without breaking
Here's where component thinking pays dividends: when the client comes back in 6 months wanting to add a new service, a blog category, or a landing page.
Without components, that new page requires designing from scratch, writing new HTML, creating new CSS, and hoping it's consistent with the rest of the site. Estimated time: 4–8 hours.
With components, you compose existing pieces: section header + image-text row + CTA section + testimonial block. The only new work is the content. Estimated time: 45–90 minutes.
That's not just faster — it's guaranteed consistent. The new page can't look different from existing pages because it uses the same building blocks.
Next: Data Flow Integrity and CMS Architecture — how the content behind your components should be structured for resilience and scalability.