iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Composition

Composition is the React way of reusing UI: build complex components by combining smaller ones via children and slot-style props, instead of extending classes or threading config through a single mega-component. Three patterns cover almost every case: children passthrough, named slot props, and compound components.

Three composition patterns side by side

EXAMPLE
// 1) Children passthrough — simplest reuse
function Card({ children }) {
  return <div className='rounded-xl border p-4 shadow-sm'>{children}</div>;
}

// 2) Named slot props — for layouts with multiple regions
function PageShell({ header, sidebar, children, footer }) {
  return (
    <div className='grid grid-rows-[auto_1fr_auto] grid-cols-[200px_1fr] h-screen'>
      <header className='col-span-2 border-b p-3'>{header}</header>
      <aside className='border-r p-3'>{sidebar}</aside>
      <main className='p-3 overflow-auto'>{children}</main>
      <footer className='col-span-2 border-t p-2 text-sm'>{footer}</footer>
    </div>
  );
}

// 3) Compound components — coordinated pieces sharing implicit state
import { createContext, useContext, useState } from 'react';
const TabsCtx = createContext(null);

function Tabs({ defaultValue, children }) {
  const [active, setActive] = useState(defaultValue);
  return (
    <TabsCtx.Provider value={{ active, setActive }}>
      <div className='tabs'>{children}</div>
    </TabsCtx.Provider>
  );
}
Tabs.List = ({ children }) => <div role='tablist' className='flex gap-2'>{children}</div>;
Tabs.Trigger = ({ value, children }) => {
  const { active, setActive } = useContext(TabsCtx);
  return (
    <button role='tab' aria-selected={active === value}
      onClick={() => setActive(value)}
      className={active === value ? 'font-bold' : 'opacity-60'}>
      {children}
    </button>
  );
};
Tabs.Panel = ({ value, children }) => {
  const { active } = useContext(TabsCtx);
  return active === value ? <div role='tabpanel'>{children}</div> : null;
};

// Usage stitches them together — no prop drilling, no inheritance
export default function App() {
  return (
    <PageShell
      header={<h1>Dashboard</h1>}
      sidebar={<nav>links</nav>}
      footer={<small>(c) 2026</small>}
    >
      <Card>
        <Tabs defaultValue='overview'>
          <Tabs.List>
            <Tabs.Trigger value='overview'>Overview</Tabs.Trigger>
            <Tabs.Trigger value='activity'>Activity</Tabs.Trigger>
          </Tabs.List>
          <Tabs.Panel value='overview'>Summary metrics here.</Tabs.Panel>
          <Tabs.Panel value='activity'>Recent activity here.</Tabs.Panel>
        </Tabs>
      </Card>
    </PageShell>
  );
}

Why it matters

Reach for compound components only when the parts truly need shared state (Tabs, Accordion, Select). If they do not, children or slot props give the same flexibility with less indirection and they tree-shake better.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
function Page({ header, children, footer }) {
    return <><Hdr>{header}</Hdr><Main>{children}</Main><Ftr>{footer}</Ftr></>;
}
Try it Yourself »

Discussion

Loading…