Deft
Ask AI

Search

Search skills, authors and topics

vercel-labs / agent-skills

vercel-composition-patterns

React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.

Install

npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-composition-patterns
title
Avoid Boolean Prop Proliferation
impact
CRITICAL
impactDescription
prevents unmaintainable component variants
tags
composition, props, architecture

Avoid Boolean Prop Proliferation

Don't add boolean props like isThread, isEditing, isDMThread to customize component behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.

Incorrect (boolean props create exponential complexity):

tsx
function Composer({  onSubmit,  isThread,  channelId,  isDMThread,  dmId,  isEditing,  isForwarding,}: Props) {  return (    <form>      <Header />      <Input />      {isDMThread ? (        <AlsoSendToDMField id={dmId} />      ) : isThread ? (        <AlsoSendToChannelField id={channelId} />      ) : null}      {isEditing ? (        <EditActions />      ) : isForwarding ? (        <ForwardActions />      ) : (        <DefaultActions />      )}      <Footer onSubmit={onSubmit} />    </form>  )}

Correct (composition eliminates conditionals):

tsx
// Channel composerfunction ChannelComposer() {  return (    <Composer.Frame>      <Composer.Header />      <Composer.Input />      <Composer.Footer>        <Composer.Attachments />        <Composer.Formatting />        <Composer.Emojis />        <Composer.Submit />      </Composer.Footer>    </Composer.Frame>  )}
// Thread composer - adds "also send to channel" fieldfunction ThreadComposer({ channelId }: { channelId: string }) {  return (    <Composer.Frame>      <Composer.Header />      <Composer.Input />      <AlsoSendToChannelField id={channelId} />      <Composer.Footer>        <Composer.Formatting />        <Composer.Emojis />        <Composer.Submit />      </Composer.Footer>    </Composer.Frame>  )}
// Edit composer - different footer actionsfunction EditComposer() {  return (    <Composer.Frame>      <Composer.Input />      <Composer.Footer>        <Composer.Formatting />        <Composer.Emojis />        <Composer.CancelEdit />        <Composer.SaveEdit />      </Composer.Footer>    </Composer.Frame>  )}

Each variant is explicit about what it renders. We can share internals without sharing a single monolithic parent.