slots
Skill3.9k starsChanged 3 days agoMITView on GitHub
Use when: building, modifying, or wrapping compound components that use child-component "slots" in Primer React. Covers when to add a `__SLOT__` marker, the `useSlots` hook, the `isSlot` helper, the `asSlot` wrapper helper, naming conventions for slot symbols, and common pitfalls.
We split this skill into the rules it gives an agent
16 rules. About 0 are common, 2 are distinctive, and 14 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
14
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Building a slot sub-component?: Add a __SLOT__ = Symbol('Parent.Slot') marker so it can be wrapped by consumers.
- Very specific: line 14
Wrapping an existing slot component?: Use asSlot(wrapper, ParentSlotComponent) to copy the marker.
- Very specific: line 17
All four primitives are exported publicly from @primer/react.
- Very specific: line 52
Use useSlots. It returns [slots, rest], where slots is keyed by your config and rest is everything else (in encounter order).
- Very specific: line 89
Add a __SLOT__ symbol after the component declaration so wrappers and parent scanners can recognise it.
- Very specific: line 117
Use lifecycle annotations (Symbol('DEPRECATED_X')) — handle deprecation via other means.
- Very specific: line 118
Use camelCase without separators (Symbol('ActionListItem') instead of Symbol('ActionList.Item')).
- Very specific: line 119
Add a __SLOT__ marker to a root component that no other component scans for — the marker is dead weight and confusing for anyone searching …
- Distinctive: line 123
Add it when either:
- Very specific: line 128
Don't add it to top-level root components unless they're explicitly used as a child somewhere (e.g.
- Very specific: line 132
When consumers wrap a Primer slot component, the wrapper must carry the same __SLOT__ marker for the parent scanner to recognise it.
- Very specific: line 168
If you need to check whether an element matches a particular slot outside of useSlots (e.g.
- Distinctive: line 191
Duplicate slot: if two children match the same slot key, only the first is rendered and a warning fires.
- Very specific: line 192
displayNamemismatch: if a child's displayName matches a slot component's displayName but the child is missing the __SLOT__ marker, a … - Very specific: line 199
Don't add __SLOT__ to the root unless another component scans for it.
- Very specific: line 200
If the new component will be wrapped downstream, document that consumers should use asSlot (or call out which slot to pass).
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Slots in Primer React
Primer React uses a lightweight, SSR-compatible slot system to let compound components extract specific child elements (e.g. <ActionList.Item> extracting <ActionList.LeadingVisual> out of its children). This skill documents the conventions and the public API surface.
TL;DR
- Building a compound component that needs to find specific children? Use
useSlots(children, config). - Building a slot sub-component? Add a
__SLOT__ = Symbol('Parent.Slot')marker so it can be wrapped by consumers. - Wrapping an existing slot component? Use
asSlot(wrapper, ParentSlotComponent)to copy the marker. - Branching on whether a child is a slot, outside of
useSlots? UseisSlot(child, SlotComponent).
All four primitives are exported publicly from @primer/react.
Architecture
| File | Purpose |
hooks/useSlots.ts | Hook that extracts named slot children from children |
utils/is-slot.ts | isSlot(element, slot) predicate |
utils/as-slot.ts | asSlot(component, source) marker-copy helper |
utils/types/Slots.ts | SlotMarker, WithSlotMarker<T>, FCWithSlotMarker<P> types |
Public exports
import {useSlots, isSlot, asSlot, type SlotMarker, type WithSlotMarker, type FCWithSlotMarker} from '@primer/react'
// or
import {useSlots} from '@primer/react/hooks'When to use slots
Slots are the right tool when:
- A parent compound component needs to extract specific named children and render them in particular positions (e.g.
Dialog.Header,Dialog.Body,Dialog.Footer). - The extraction must survive SSR (you can't rely on refs or rendered DOM).
- Consumers should be able to wrap your slot sub-components without breaking the parent's matching.
Slots are not the right tool when:
- You just want to render
childrenas-is. - You need to find all children of a given type (
useSlotsis single-match-per-key — see "Limitations"). - You need to inspect grandchildren or do per-child transforms with side effects (see
ActionMenufor the pattern when you have to).
Building a slot consumer (parent component)
Use useSlots. It returns [slots, rest], where slots is keyed by your config and rest is everything else (in encounter order).
import {useSlots} from '@primer/react/hooks'
function MyCompound({children}: {children: React.ReactNode}) {
const [slots, rest] = useSlots(children, {
header: MyCompound.Header,
footer: MyCompound.Footer,
})
return (
<div>
{slots.header}
<div className="body">{rest}</div>
{slots.footer}
</div>
)
}Matcher styles
useSlots(children, {
// 1. Component reference (most common)
header: Header,
// 2. Component + props test fn (variant matching)
block: [Description, props => props.variant === 'block'],
})Each slot key produces ReactElement | undefined. Slots are single-match: the first matching child wins; duplicates emit a dev-mode warning.
Building a slot sub-component (child component)
Add a __SLOT__ symbol after the component declaration so wrappers and parent scanners can recognise it. Use Parent.Slot for the symbol description and the WithSlotMarker / FCWithSlotMarker types for the export.
import type {FCWithSlotMarker} from '@primer/react'
export const MyHeader: FCWithSlotMarker<MyHeaderProps> = (props) => { ... }
MyHeader.displayName = 'MyCompound.Header'
MyHeader.__SLOT__ = Symbol('MyCompound.Header')For forwardRef'd or otherwise non-FC components, assert with WithSlotMarker<typeof Component>:
;(MyHeader as WithSlotMarker<typeof MyHeader>).__SLOT__ = Symbol('MyCompound.Header')Naming conventions for Symbol(...) descriptions
The Symbol description shows up in React DevTools and crash logs. Keep it predictable:
| Component shape | Convention | Example |
| Sub-component of a compound | Symbol('Parent.Slot') | Symbol('ActionList.LeadingVisual') |
| Root component used as a child of another component | Symbol('Component') | Symbol('FormControl') (FormControl is scanned as a child by CheckboxGroup) |
| Deeply nested slot | Symbol('Parent.Slot.SubSlot') | Symbol('ActionList.GroupHeading.TrailingAction') |
Don't:
- Use lifecycle annotations (
Symbol('DEPRECATED_X')) — handle deprecation via other means. - Use camelCase without separators (
Symbol('ActionListItem')instead ofSymbol('ActionList.Item')). - Add a
__SLOT__marker to a root component that no other component scans for — the marker is dead weight and confusing for anyone searching the codebase.
Should I add a __SLOT__ marker at all?
Add it when either:
- A parent in this codebase scans for it (use
grep "isSlot.*ComponentName"to check), or - The component is a documented compound sub-component (e.g.
Dialog.Header) that consumers may want to wrap. Sub-component markers are intentionally generous because wrapping is a common downstream pattern.
Don't add it to top-level root components unless they're explicitly used as a child somewhere (e.g. FormControl is, because CheckboxGroup scans for it).
Wrapping a slot component (asSlot)
When consumers wrap a Primer slot component, the wrapper must carry the same __SLOT__ marker for the parent scanner to recognise it. Use asSlot:
import {asSlot, ActionList} from '@primer/react'
const ColoredLeadingVisual = asSlot(
function ColoredLeadingVisual({color, children}: {color: string; children: React.ReactNode}) {
return (
<ActionList.LeadingVisual>
<span style={{color}}>{children}</span>
</ActionList.LeadingVisual>
)
},
ActionList.LeadingVisual,
)
// Now ActionList.Item recognises ColoredLeadingVisual as a leading visual slot.
<ActionList.Item>
<ColoredLeadingVisual color="red"><CheckIcon /></ColoredLeadingVisual>
Approved
</ActionList.Item>asSlot is the preferred replacement for the older cast-heavy pattern:
// ❌ Old, footgun-prone
;(ColoredLeadingVisual as typeof ColoredLeadingVisual & {__SLOT__?: symbol}).__SLOT__ =
ActionList.LeadingVisual.__SLOT__
// ✅ New, typed, dev-warns if the source has no marker
const ColoredLeadingVisual = asSlot(ColoredLeadingVisual, ActionList.LeadingVisual)Branching on slot membership (isSlot)
If you need to check whether an element matches a particular slot outside of useSlots (e.g. inside an existing Children.map with side effects), use isSlot:
import {isSlot} from '@primer/react'
React.Children.map(children, child => {
if (React.isValidElement(child) && isSlot(child, Tooltip)) {
// ...
}
})isSlot matches by comparing __SLOT__ markers (on the element or its type), so it recognises both the original slot component and any wrapper created with asSlot. It does not compare component identity (child.type === Tooltip) directly — that works incidentally only because the original component carries the same marker symbol.
Limitations and follow-ups
useSlotsis single-match. It cannot collect all children of a given type into an array. Consumers that need that pattern (e.g.UnderlinePanelscollecting allTabchildren,CheckboxGroupcollecting allFormControlchildren) currently hand-roll the logic. A future extension touseSlotsmay add a multi-match option.- Per-child classification / side-effect transforms (e.g.
ActionMenurewriting Tooltip-wrapped Anchors) are not a slot pattern — keep them as bespokeChildren.maploops withisSlotpredicates. - Avoid calling
useSlotspurely for dev-mode warnings in render-hot paths. AlthoughuseSlotsis implemented as a plain function (no internal React hooks), running it on every render just to compute a dev-only assertion is wasteful — call it in the regular render flow when you also need its output. - Don't call
useSlotsinsideuseMemo,useCallback,useEffect, or any other callback. Thereact-hooks/rules-of-hookslint rule enforces this based on theuse*naming convention, even thoughuseSlotsitself doesn't call any React hooks. Always call it at the top level of your component or custom hook. If you need to extract slots before a memoised computation, calluseSlotsfirst and then reference the result insideuseMemo.
Dev-mode warnings emitted by useSlots
- Duplicate slot: if two children match the same slot key, only the first is rendered and a warning fires.
displayNamemismatch: if a child'sdisplayNamematches a slot component'sdisplayNamebut the child is missing the__SLOT__marker, a warning fires suggestingasSlot. This catches the most common wrapping footgun.
Quick checklist for a new compound component
- Define the sub-components (
MyCompound.Header, etc.) withdisplayNameset and__SLOT__ = Symbol('MyCompound.Header')applied. - In the parent, call
useSlots(children, {header: MyCompound.Header, ...})once and renderslots.header/restin the right places. - Export
MyCompoundwithObject.assign(Root, {Header, Footer, ...}). - Don't add
__SLOT__to the root unless another component scans for it. - If the new component will be wrapped downstream, document that consumers should use
asSlot(or call out which slot to pass).
Trending
More talk lately.about the whole repository
Who's talking about primer/react
- vercel/next.js“Turbopack Sass loader does not resolve imports correctly”GitHub · Jan 1, 2024 · 7 reactions
- mui/mui-design-kits“[figma] Support Figma Code Connect ”GitHub · Jun 28, 2024 · 5 reactions
- filipedeschamps/tabnews.com.br“feat(primer/react): update version”GitHub · Dec 20, 2023 · 2 reactions
- github/docs“Fix theme mismatch”GitHub · May 18, 2023 · 2 reactions
- primer/view_components“Component status page”GitHub · Nov 29, 2021 · 2 reactions
The biggest 5 of 36 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 36
Talked about on GitHub (36 times).
On GitHub · 36
- Jan 1, 2024Turbopack Sass loader does not resolve imports correctly — vercel/next.js7 reactions · 11 comments
- Jun 28, 2024[figma] Support Figma Code Connect — mui/mui-design-kits5 reactions · 5 comments
- Dec 20, 2023feat(primer/react): update version — filipedeschamps/tabnews.com.br2 reactions · 10 comments
- May 18, 2023Fix theme mismatch — github/docs2 reactions · 13 comments
- Nov 29, 2021Component status page — primer/view_components2 reactions · 7 comments
31 more on GitHub
- Mar 9, 2026Bump the app group with 8 updates — CRRU-UK/photo-id-app1 reaction · 2 comments
- Feb 12, 2026Add `AGENTS.md` — carbon-design-system/carbon1 reaction · 1 comment
- Feb 2, 2026Bump the app group with 13 updates — CRRU-UK/photo-id-app1 reaction · 2 comments
- Nov 10, 2025deps: bump @primer/react from 37.31.0 to 38.1.0 — npm/documentation1 reaction · 4 comments
- Mar 3, 2025Update release_conductor.yml — primer/.github1 reaction
- Oct 17, 2024Docs: add a11y guidance regarding required fields — primer/design1 reaction · 2 comments
- Oct 15, 2024Fix bug where checkboxes were broken with prefers-reduced-motion — primer/view_components1 reaction · 1 comment
- Aug 30, 2024Atualiza as propriedades do componente `Tooltip` para serem compatíveis com a nova versão (`Tooltip v2`) — filipedeschamps/tabnews.com.br1 reaction · 1 comment
- Mar 28, 2024Tooltip: Update the threshold value to match with the offset — primer/view_components1 reaction · 1 comment
- Feb 28, 2023Show source code in React component examples — primer/design1 reaction
- Dec 13, 2022Add a treatment for consistent appearance of content on focus — primer/design1 reaction · 1 comment
- Nov 9, 2022Using playwright to generate snapshots — primer/view_components1 reaction · 2 comments
- Oct 19, 2022Focus zone calls error in vitest + jsdom — primer/behaviors1 reaction · 2 comments
- Oct 11, 2022Atualiza todas as dependências possíveis — filipedeschamps/tabnews.com.br1 reaction · 2 comments
- Aug 19, 2022Add a CommonJS build to the package — github/combobox-nav1 reaction · 6 comments
- Feb 16, 2022ModuleNotFoundError: Could not find module in path "" relative to "" — codesandbox/codesandbox-client1 reaction · 9 comments
- Oct 29, 2026Create: Timeline in Figma — deboramoratalla-lab/ds-lab0 reactions
- Oct 26, 2026🐛 cheatcode_install registers an MCP server with its repo URL as the command and reports success — trustmybot/plugin0 reactions
- Oct 19, 2026Add named capture modes with mode-keyed baselines (bet) — ravidsrk/frontguard0 reactions
- Oct 15, 2026Rule proposal: Enforce consistency between documented jsDoc default and actual default values — typescript-eslint/typescript-eslint0 reactions · 2 comments
- Sep 26, 2026Refatorações e ajustes finais da Milestone 3 — jeancarlier/clone-tabnews0 reactions
- Jun 24, 2026This project is not maintained anymore 😢 — github/command0 reactions · 1 comment
- Jun 11, 2026WS2 · Regenerate primer-react through the corrected registry-scoped meta-skill (HITL) — vercel-labs/ds-skill-extraction-workshop0 reactions · 3 comments
- Feb 6, 2026Upgrade React to 19, additional major dependency updates (Primer, TanStack, tRPC) — github-community-projects/private-mirrors0 reactions · 3 comments
- Nov 21, 2025User interface — panodata/suraya0 reactions · 4 comments
- Apr 11, 2025✨ 이슈 상세 페이지 UI 개선 — Seono-Na/demo-issue-api-playground0 reactions
- Mar 21, 2025IconButton should include Tooltip by default — benbalter/alt-text-generator-action0 reactions
- Mar 21, 2024Improve SSR by removing Webpack dependency — github-community-projects/org-metrics-dashboard0 reactions
- Jan 11, 2024Other relevant component libraries/frameworks — crate/crate-ui-components0 reactions
- Jan 5, 2024Add a pull request template — deven-org/boiler0 reactions
- Sep 23, 2022Element should not auto-register itself unless a browser context is found — github/markdown-toolbar-element0 reactions · 8 comments
In this repository17 other files it gives its agents.
- .github/copilot-instructions.mdCopilot instructions
- agentic-workflowsSkill
- changesetsSkill
- component-testingSkill
- create-adr-migratorSkill
- deprecationsSkill
- feature-flagsSkill
- modular-ds-accessibility-contractSkill
- modular-ds-base-componentsSkill
- modular-ds-config-componentsSkill
- modular-ds-presentational-componentsSkill
- modular-ds-spectrum-modelSkill
- modular-ds-utilitiesSkill
- storybookSkill
- style-guideSkill
- turborepoSkill
- write-skillSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

