Component-driven development has matured dramatically in the last decade. As frameworks evolve, so too do the paradigms and principles that shape how we build UI systems. One concept that has stood the test of time—arguably even strengthened—is composability.
Radix UI for example, embodies this philosophy beautifully:
import { Accordion } from "radix-ui";
export default () => (
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger />
</Accordion.Header>
<Accordion.Content />
</Accordion.Item>
</Accordion.Root>
);
A common pattern I like to use are primitive parts to make up a card.
<Card>
<CardHeader />
<CardBody />
<CardFooter />
</Card>
Rather than bloating a single <Card />
component with endless props (showHeader
, footerVariant
, isCompact
), Radix opts for composability. Each subcomponent does one thing well, and you stitch them together to achieve flexibility.
This reflects principles drawn from functional programming and single responsibility, while also challenging outdated dogma like DRY (Don't Repeat Yourself).
Why "God Components" Fail
Many React developers fall into the trap of the "God Component":
- Takes a huge object of props.
- Bloated with
if/else
conditionals. - Hard to test, hard to extend, brittle to refactors.
Consider this anti-pattern:
<Card
showHeader
footerVariant="compact"
theme="dark"
showBorder={false}
isLoading
>
Content goes here
</Card>
This single component is burdened with far too much knowledge. Even worse, when props are passed as new object references ({}
), React often sees them as “different” on each render, causing unnecessary re-renders. Objects-as-props are another subtle performance trap.
By contrast, composability avoids these pitfalls:
<Card>
<CardHeader>Profile</CardHeader>
<CardBody>Some content</CardBody>
<CardFooter compact>Footer text</CardFooter>
</Card>
- Each part is responsible for itself.
- Props stay shallow and predictable.
- Testing is trivial: test
<CardHeader />
in isolation.
Composability Over DRY
"Don’t Repeat Yourself" (DRY) once dominated software engineering. In UI, however, repetition can be healthy. Consistency of patterns matters more than absolute deduplication.
For example, repeating the structure of composable layouts—like headers, bodies, and footers—creates familiarity. This makes it easier for other developers to navigate your codebase and for design systems to scale. Over-abstraction in the name of DRY often introduces more indirection and fragility than it removes.
Inspirations from Functional Programming
Composability mirrors functional programming’s ethos:
- Small, pure functions → small, pure components.
- Pipeline composition → JSX composition.
- No side effects → predictable rendering.
This mindset makes components easy to test, reason about, and reuse. Just as FP warns against large impure functions, frontend engineers should resist "God Components."
Composability in VueJS
React isn’t the only framework where this philosophy thrives. Vue has long encouraged composability via slots and, more recently, the Composition API.
A Vue <Card>
might look like:
<Card>
<template #header>
<CardHeader>Profile</CardHeader>
</template>
<CardBody>Some content</CardBody>
<template #footer>
<CardFooter compact>Footer text</CardFooter>
</template>
</Card>
Key parallels:
- Vue’s slots are React’s children.
- Vue’s Composition API encourages extracting small, reusable logic units, just like React hooks.
- Both frameworks are moving away from monolithic components toward patterns over props.
Lessons from React translate directly into Vue:
- Avoid God Components.
- Embrace composable slots/components.
- Repetition of patterns is good—don’t fear repeating yourself.
Is Composability Still Relevant in React Server Components (RSC)?
With Next.js and RSC, some devs worry composability may be less relevant. After all, server components bring new paradigms: data fetching in the component, zero client-side JavaScript by default.
But composability remains core:
- Server components benefit even more from composability: smaller components fetch their own data, keep concerns isolated.
- Streaming UI aligns with composability: headers can load before footers, independent of one another.
-
Client components wrap naturally into the same patterns. A
CardHeader
can be a client component for interactivity, whileCardBody
stays a server component for static content.
In fact, the move to RSC makes "God Components" even riskier. Bloated components become harder to cache, harder to stream, and harder to split between client/server boundaries.
Closing Thoughts
Composability isn’t just a coding style—it’s a survival strategy for scalable UI systems. From React’s Radix UI to Vue’s slots, the philosophy of small, focused, repeatable patterns is stronger than ever.
- God Components are brittle; composability is robust.
- DRY is overrated; repetition of patterns fosters clarity.
- Functional programming principles map perfectly onto modern component design.
- Composability transcends libraries/frameworks: React/NextJS and Vue /Nuxt converge here.
- Even in the age of React Server Components, composability is more critical, not less.
The takeaway: keep building small, composable, repeatable components. Your future self—and your teammates—will thank you.
Top comments (0)