react-composition-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited react-composition-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Build flexible, maintainable React components using compound components, context providers, and explicit variants. Avoid boolean prop proliferation.
Composition patterns that scale:
composition, compound components, context, provider, boolean props, variants, react patterns, component architecture, render props, children
Source: Vercel Engineering
npx clawhub@latest install composition-patternsAvoid boolean prop proliferation. Each boolean doubles possible states.
// BAD: 4 booleans = 16 possible states
<Composer isThread isDMThread isEditing isForwarding />
// GOOD: Explicit variants, clear intent
<ThreadComposer channelId="abc" />
<EditComposer messageId="xyz" />Structure complex components with shared context. Consumers compose what they need.
const ComposerContext = createContext<ComposerContextValue | null>(null)
// Provider handles state
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return (
<ComposerContext value={{ state, actions, meta }}>
{children}
</ComposerContext>
)
}
// Subcomponents access context
function ComposerInput() {
const { state, actions: { update }, meta: { inputRef } } = use(ComposerContext)
return (
<TextInput
ref={inputRef}
value={state.input}
onChangeText={(text) => update(s => ({ ...s, input: text }))}
/>
)
}
function ComposerSubmit() {
const { actions: { submit } } = use(ComposerContext)
return <Button onPress={submit}>Send</Button>
}
// Export as namespace
const Composer = {
Provider: ComposerProvider,
Frame: ComposerFrame,
Input: ComposerInput,
Submit: ComposerSubmit,
Header: ComposerHeader,
Footer: ComposerFooter,
}Usage:
<Composer.Provider state={state} actions={actions} meta={meta}>
<Composer.Frame>
<Composer.Header />
<Composer.Input />
<Composer.Footer>
<Composer.Formatting />
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
</Composer.Provider>Define a contract any provider can implement: state, actions, meta.
interface ComposerState {
input: string
attachments: Attachment[]
isSubmitting: boolean
}
interface ComposerActions {
update: (updater: (state: ComposerState) => ComposerState) => void
submit: () => void
}
interface ComposerMeta {
inputRef: React.RefObject<TextInput>
}
interface ComposerContextValue {
state: ComposerState
actions: ComposerActions
meta: ComposerMeta
}Same UI, different providers:
// Local state provider
function ForwardMessageProvider({ children }) {
const [state, setState] = useState(initialState)
return (
<ComposerContext value={{
state,
actions: { update: setState, submit: useForwardMessage() },
meta: { inputRef: useRef(null) },
}}>
{children}
</ComposerContext>
)
}
// Global synced state provider
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
return (
<ComposerContext value={{
state,
actions: { update, submit },
meta: { inputRef: useRef(null) },
}}>
{children}
</ComposerContext>
)
}Both work with the same <Composer.Input /> component.
Create named components for each use case instead of boolean modes.
// BAD: What does this render?
<Composer
isThread
isEditing={false}
channelId="abc"
showAttachments
/>
// GOOD: Self-documenting
<ThreadComposer channelId="abc" />Implementation:
function ThreadComposer({ channelId }: { channelId: string }) {
return (
<ThreadProvider channelId={channelId}>
<Composer.Frame>
<Composer.Input />
<AlsoSendToChannelField channelId={channelId} />
<Composer.Footer>
<Composer.Formatting />
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
</ThreadProvider>
)
}
function EditComposer({ messageId }: { messageId: string }) {
return (
<EditProvider messageId={messageId}>
<Composer.Frame>
<Composer.Input />
<Composer.Footer>
<Composer.CancelEdit />
<Composer.SaveEdit />
</Composer.Footer>
</Composer.Frame>
</EditProvider>
)
}Components outside the visual hierarchy can access state via provider.
function ForwardMessageDialog() {
return (
<ForwardMessageProvider>
<Dialog>
{/* Composer UI */}
<Composer.Frame>
<Composer.Input placeholder="Add a message" />
<Composer.Footer>
<Composer.Formatting />
</Composer.Footer>
</Composer.Frame>
{/* Preview OUTSIDE composer but reads its state */}
<MessagePreview />
{/* Actions OUTSIDE composer but can submit */}
<DialogActions>
<CancelButton />
<ForwardButton />
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
}
// Can access context despite being outside Composer.Frame
function ForwardButton() {
const { actions: { submit } } = use(ComposerContext)
return <Button onPress={submit}>Forward</Button>
}
function MessagePreview() {
const { state } = use(ComposerContext)
return <Preview message={state.input} attachments={state.attachments} />
}Key insight: Provider boundary matters, not visual nesting.
Use children for composition, render props only when passing data.
// BAD: Render props for structure
<Composer
renderHeader={() => <CustomHeader />}
renderFooter={() => <Formatting />}
renderActions={() => <Submit />}
/>
// GOOD: Children for structure
<Composer.Frame>
<CustomHeader />
<Composer.Input />
<Composer.Footer>
<Formatting />
<Submit />
</Composer.Footer>
</Composer.Frame>When render props ARE appropriate:
// Passing data to children
<List
data={items}
renderItem={({ item, index }) => <Item item={item} index={index} />}
/>Only the provider knows how state is managed. UI consumes the interface.
// BAD: UI coupled to state implementation
function ChannelComposer({ channelId }) {
const state = useGlobalChannelState(channelId) // Knows about global state
const { submit } = useChannelSync(channelId) // Knows about sync
return <Composer.Input value={state.input} onChange={...} />
}
// GOOD: State isolated in provider
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
return (
<Composer.Provider
state={state}
actions={{ update, submit }}
meta={{ inputRef: useRef(null) }}
>
{children}
</Composer.Provider>
)
}
// UI only knows the interface
function ChannelComposer() {
return (
<Composer.Frame>
<Composer.Input /> {/* Works with any provider */}
<Composer.Submit />
</Composer.Frame>
)
}| Anti-Pattern | Solution |
|---|---|
| Boolean props | Explicit variant components |
| Render props for structure | Children composition |
| State in component | Lift to provider |
| Coupled to state impl | Generic context interface |
| Many conditional renders | Compose pieces explicitly |
rules/architecture-avoid-boolean-props.md - Detailed boolean prop guidancerules/architecture-compound-components.md - Compound component patternrules/state-context-interface.md - Context interface designrules/state-decouple-implementation.md - State isolationrules/state-lift-state.md - Provider patternrules/patterns-explicit-variants.md - Variant componentsrules/patterns-children-over-render-props.md - Composition over callbacks~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.