Skip to content

Self-contained Components

A component is responsible for its own inside, never for its place in the outside world. It should blend into any environment it is dropped into — a grid, a flex row, a sidebar — without disturbing its surroundings.

This is the principle behind the Override Classes pattern: it explains why a parent, not the component, controls layout and outer spacing.

Two rules

1. A component blends into any environment

A component does not decide how it sits in its parent's layout:

  • its placement — no position, float, or grid placement that positions it within the parent;
  • the space around it — outer margins to its neighbours belong to the parent.

Its intrinsic size and its internal layout stay entirely the component's own — this rule is only about how it behaves toward the outside.

This is what makes a component reusable: the same UiCard can sit in a 3-column grid, a modal, or a full-width section without any change.

2. A component's styles never leak outside

The classic case is the outer margin. A component that carries its own margin-right becomes unpredictable: the margin is there even when it isn't wanted, it can collapse in surprising ways, and it couples the component to one specific context.

WARNING

Never set an outer margin (or positioning) inside a component. Outer spacing is contextual — it belongs to the parent.

Where does spacing live?

ConcernExampleOwner
Inner spacingpadding, gap between the component's own elementsThe component
Outer spacingmargin to neighbours, column span, alignmentThe parent

In Tailwind, the idiom that goes with this principle is gap on the parent container rather than margins on the children. It keeps every child margin-free by design.

❌ The component pushes space outward

tsx
// UiCard.tsx — the card imposes a margin on whatever follows it
const classes = tv({
  slots: {
    root: "mb-6 flex flex-col gap-3", // ❌ mb-6 leaks into the layout
  },
});

✅ The parent owns the spacing between components

tsx
// The parent spaces its children with gap — cards stay margin-free
<div className="flex flex-col gap-6">
  <UiCard />
  <UiCard />
</div>

How the parent applies layout

When the parent needs to position or space a component, it does so from the outside — it does not reach into the component's styles. Two channels:

  • Grid placement — utility classes like col-span-* applied by the parent, as shown in Layout.
  • Contextual adjustments — the overrideClasses prop, which exposes the root slot precisely so the parent can inject positioning and outer spacing.
tsx
// The parent decides where the card goes and how it sits in the grid
<UiCard
  overrideClasses={{
    root: "col-span-4 lg:col-span-6 mt-auto", // positioning + outer spacing, from the parent
  }}
/>

TIP

This is exactly why Override Classes recommends always exposing root: it is the seam through which the parent takes responsibility for layout.

Why it matters

  • Reusability — a context-agnostic component works everywhere without tweaks.
  • Predictability — no margins leaking or collapsing where you didn't expect them.
  • Clear ownership — the parent composes the layout; the component renders itself. Each side has one job.