react-composition — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited react-composition (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.
Composition patterns for building flexible, maintainable React components. Avoid boolean prop proliferation by using compound components, lifting state, and composing internals. These patterns make codebases easier to work with as they scale.
| # | Pattern | Impact |
|---|---|---|
| 1 | Avoid Boolean Props | CRITICAL |
| 2 | Compound Components | HIGH |
| 3 | Context Interface (DI) | HIGH |
| 4 | State Lifting | HIGH |
| 5 | Explicit Variants | MEDIUM |
| 6 | Children Over Render Props | MEDIUM |
npx clawhub@latest install react-compositionDon't add boolean props like isThread, isEditing, isDMThread to customize behavior. Each boolean doubles possible states and creates unmaintainable conditional logic. Use composition instead.
// BAD — boolean props create exponential complexity
function Composer({ isThread, isDMThread, isEditing, isForwarding }: Props) {
return (
<form>
<Input />
{isDMThread ? <AlsoSendToDMField /> : isThread ? <AlsoSendToChannelField /> : null}
{isEditing ? <EditActions /> : isForwarding ? <ForwardActions /> : <DefaultActions />}
</form>
)
}
// GOOD — composition eliminates conditionals
function ChannelComposer() {
return (
<Composer.Frame>
<Composer.Input />
<Composer.Footer><Composer.Attachments /><Composer.Submit /></Composer.Footer>
</Composer.Frame>
)
}
function ThreadComposer({ channelId }: { channelId: string }) {
return (
<Composer.Frame>
<Composer.Input />
<AlsoSendToChannelField id={channelId} />
<Composer.Footer><Composer.Submit /></Composer.Footer>
</Composer.Frame>
)
}Each variant is explicit about what it renders. Shared internals without a monolithic parent.
Structure complex components with shared context. Each subcomponent accesses state via context, not props. Export as a namespace object.
const ComposerContext = createContext<ComposerContextValue | null>(null)
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return <ComposerContext value={{ state, actions, meta }}>{children}</ComposerContext>
}
function ComposerInput() {
const { state, actions: { update }, meta: { inputRef } } = use(ComposerContext)
return <TextInput ref={inputRef} value={state.input}
onChangeText={(t) => update((s) => ({ ...s, input: t }))} />
}
const Composer = {
Provider: ComposerProvider, Frame: ComposerFrame,
Input: ComposerInput, Submit: ComposerSubmit, Footer: ComposerFooter,
}
// Consumers compose exactly what they need
<Composer.Provider state={state} actions={actions} meta={meta}>
<Composer.Frame>
<Composer.Input />
<Composer.Footer><Composer.Formatting /><Composer.Submit /></Composer.Footer>
</Composer.Frame>
</Composer.Provider>Define a generic interface with state, actions, and meta. Any provider implements this contract — enabling the same UI to work with different state implementations. The provider is the only place that knows how state is managed.
interface ComposerContextValue {
state: { input: string; attachments: Attachment[]; isSubmitting: boolean }
actions: { update: (fn: (s: ComposerState) => ComposerState) => void; submit: () => void }
meta: { inputRef: React.RefObject<TextInput> }
}
// Provider A: Local state for ephemeral forms
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
return (
<ComposerContext value={{ state, actions: { update: setState, submit: useForwardMessage() },
meta: { inputRef: useRef(null) } }}>{children}</ComposerContext>
)
}
// Provider B: Global synced state for channels
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
return (
<ComposerContext value={{ state, actions: { update, submit },
meta: { inputRef: useRef(null) } }}>{children}</ComposerContext>
)
}Swap the provider, keep the UI. Same Composer.Input works with both.
Move state into dedicated provider components so sibling components outside the main UI can access and modify state without prop drilling or refs.
// BAD — state trapped inside component; siblings can't access it
function ForwardMessageComposer() {
const [state, setState] = useState(initialState)
return <Composer.Frame><Composer.Input /><Composer.Footer /></Composer.Frame>
}
function ForwardMessageDialog() {
return (
<Dialog>
<ForwardMessageComposer />
<MessagePreview /> {/* Can't access composer state */}
<ForwardButton /> {/* Can't call submit */}
</Dialog>
)
}
// GOOD — state lifted to provider; any descendant can access it
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const submit = useForwardMessage()
return (
<Composer.Provider state={state} actions={{ update: setState, submit }}
meta={{ inputRef: useRef(null) }}>{children}</Composer.Provider>
)
}
function ForwardMessageDialog() {
return (
<ForwardMessageProvider>
<Dialog>
<ForwardMessageComposer />
<MessagePreview /> {/* Reads state from context */}
<ForwardButton /> {/* Calls submit from context */}
</Dialog>
</ForwardMessageProvider>
)
}
function ForwardButton() {
const { actions } = use(Composer.Context)
return <Button onPress={actions.submit}>Forward</Button>
}Key insight: Components that need shared state don't have to be visually nested — they just need to be within the same provider.
Instead of one component with many boolean props, create explicit variants. Each composes the pieces it needs — self-documenting, no impossible states.
// BAD — what does this render?
<Composer isThread isEditing={false} channelId="abc" showAttachments showFormatting={false} />
// GOOD — immediately clear
<ThreadComposer channelId="abc" />
<EditMessageComposer messageId="xyz" />
<ForwardMessageComposer messageId="123" />Each variant is explicit about its provider/state, UI elements, and actions.
Use children for composition instead of renderX props. Children are more readable and compose naturally.
// BAD — render props
<Composer
renderHeader={() => <CustomHeader />}
renderFooter={() => <><Formatting /><Emojis /></>}
/>
// GOOD — children composition
<Composer.Frame>
<CustomHeader />
<Composer.Input />
<Composer.Footer><Composer.Formatting /><SubmitButton /></Composer.Footer>
</Composer.Frame>When render props are appropriate: When the parent needs to pass data back (e.g., renderItem={({ item, index }) => ...}).
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.