Skip to content

feat: normalise component displayName conventions - #7871

Draft
adierkens wants to merge 3 commits into
mainfrom
adierkens/displayname-consistency
Draft

feat: normalise component displayName conventions#7871
adierkens wants to merge 3 commits into
mainfrom
adierkens/displayname-consistency

Conversation

@adierkens

@adierkensadierkens commented May 22, 2026

Copy link
Copy Markdown
Contributor

Closes #

Normalises component displayName strings across @primer/react and adds displayName where it's actually needed — i.e. compound sub-components whose runtime function name doesn't match the canonical name users see, and forwardRef wrappers with anonymous inner arrow functions. Top-level components whose function/variable name already matches the canonical name are intentionally left alone — modern React infers displayName from Function.name and the variable assignment, so adding X.displayName = 'X' is noise.

Companion to the slot consistency work in #7869.

Changelog

New

  • .github/skills/display-name/SKILL.md — a contributor skill documenting when displayName is required, when it's redundant, the canonical Parent.Slot naming convention, the required match with Symbol(...) descriptions on slot components, and a quick decision tree. Referenced from .github/copilot-instructions.md.
  • displayName on compound sub-components where the runtime function name doesn't match the canonical name users see:
    • Visual/Heading/Description/PrimaryAction/SecondaryActionBlankslate.*
    • ActionBarIconButton/ActionBarGroup/ActionBarMenu/VerticalDividerActionBar.*
    • Description/Divider/TrailingActionActionList.*
    • ErrorDialog/Pagination/Table/TableHead/TableBody/TableHeader/TableRow/TableCell/TableCellPlaceholder/TableContainer/TableTitle/TableSubtitle/TableDivider/TableActions/TableSkeletonDataTable.*
    • FilteredActionListInput/FilteredActionListBodyLoaderFilteredActionList.*
    • FormControlCaption/FormControlLabel/FormControlLeadingVisualFormControl.*
    • SegmentedControlButton/SegmentedControlIconButtonSegmentedControl.Button/.IconButton
    • PanelSelectPanel, SelectPanelMessageSelectPanel.Message
    • StackItemStack.Item
    • TopicTagGroupTopicTag.Group

Changed

  • Renames existing sub-component displayName strings from camelCase-without-separator to the canonical Parent.Slot dot notation:
    • BannerPrimaryActionBanner.PrimaryAction
    • BannerSecondaryActionBanner.SecondaryAction
    • Summary (in Details) → Details.Summary
    • TimelineItemTimeline.Item, TimelineBodyTimeline.Body, TimelineBreakTimeline.Break
    • ParentLink (in PageHeader) → PageHeader.ParentLink
    • TitleArea (in PageHeader) → PageHeader.TitleArea
    • HorizontalDivider (in PageLayout) → PageLayout.HorizontalDivider
    • VerticalDivider (in PageLayout) → PageLayout.VerticalDivider

Removed

  • Nothing removed.

What was evaluated but intentionally not changed

Top-level components whose function or variable name already matches the canonical name we want users to see (e.g. export function Pagehead, export const VisuallyHidden = ..., const Label = forwardRef(function Label(...))) were intentionally left without an explicit displayName. Modern React + bundler inference handles these cases. The skill documents this decision and the cases where you do still want to set displayName defensively (anonymous forwardRef wrappers, minified production builds where Function.name is stripped).

Rollout strategy

  • Patch release
  • Minor release
  • Major release
  • None

No runtime behaviour changes. The renames (e.g. TimelineItemTimeline.Item) change what DevTools and error stacks display; if any downstream consumer is grepping React's element tree by string they'd need to update, but that's a non-public-API contract.

Testing & Reviewing

  • npx tsc -p packages/react/tsconfig.json --noEmit clean.
  • npx eslint on all 30 changed files clean.
  • ✅ Targeted browser-mode tests for affected components (hooks, utils, PageHeader, NavList, ActionMenu) — 199/200 pass, 1 todo.
  • The 40 snapshot failures in a full browser-mode run are all pre-existing CSS-hash drift unrelated to this change (verifiable by stashing and re-running on main).

Worth a close review:

  • .github/skills/display-name/SKILL.md — convention statements. Let me know if anything contradicts your intent, particularly the "skip displayName for top-level matching names" recommendation.
  • The DataTable rename cluster — many sub-component renames at once.

Merge checklist

@changeset-bot

changeset-botBot commented May 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 371288a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@primer/reactPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actionsgithub-actionsBot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label May 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Action required

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Check the integration testing docs for step-by-step instructions. Or, apply the integration-tests: skipped manually label to skip these checks.

To publish a canary release for integration testing, apply the Canary Release label to this PR.

@adierkensadierkens added the Canary Release Apply this label when you want CI to create a canary release of the current PR label May 22, 2026
@github-actions
github-actionsBot requested a deployment to storybook-preview-7871 May 22, 2026 19:39 Abandoned
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7871 May 22, 2026 19:50 Inactive
@adierkens
adierkens marked this pull request as ready for review May 22, 2026 20:13
CopilotAI review requested due to automatic review settings May 22, 2026 20:13
@adierkens
adierkens requested a review from a team as a code ownerMay 22, 2026 20:13

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Normalizes React component displayName usage across @primer/react, focusing on making compound sub-components consistently appear as Parent.Slot in DevTools/stacks, and adds contributor documentation describing when displayName is required vs redundant.

Changes:

  • Adds/renames displayName for many compound sub-components to the canonical Parent.Slot dot-notation.
  • Adds a new contributor skill documenting displayName conventions and links it from Copilot instructions.
  • Includes a patch changeset describing the normalization work.
Show a summary per file
FileDescription
packages/react/src/TopicTag/TopicTagGroup.tsxSets TopicTagGroup.displayName to TopicTag.Group.
packages/react/src/TooltipV2/Tooltip.tsxMinor formatting change near slot marker.
packages/react/src/Tooltip/Tooltip.tsxMinor formatting change near deprecated slot marker.
packages/react/src/Timeline/Timeline.tsxRenames displayName strings to Timeline.* dot notation.
packages/react/src/Stack/Stack.tsxAdds StackItem.displayName = 'Stack.Item'.
packages/react/src/SelectPanel/SelectPanelMessage.tsxAdds SelectPanelMessage.displayName = 'SelectPanel.Message'.
packages/react/src/SelectPanel/SelectPanel.tsxSets root Panel.displayName = 'SelectPanel' for the exported SelectPanel.
packages/react/src/SegmentedControl/SegmentedControlIconButton.tsxAdds displayName aligned with slot marker.
packages/react/src/SegmentedControl/SegmentedControlButton.tsxAdds displayName aligned with slot marker.
packages/react/src/PageLayout/PageLayout.tsxRenames divider displayName strings to PageLayout.* dot notation.
packages/react/src/PageHeader/PageHeader.tsxRenames slot-ish subcomponent displayName strings to PageHeader.*.
packages/react/src/Pagehead/Pagehead.tsxFormatting-only change (blank line).
packages/react/src/FormControl/FormControlLeadingVisual.tsxAdds displayName aligned with slot marker.
packages/react/src/FormControl/FormControlLabel.tsxAdds displayName aligned with slot marker.
packages/react/src/FormControl/FormControlCaption.tsxAdds displayName aligned with slot marker.
packages/react/src/FilteredActionList/FilteredActionListLoaders.tsxAdds FilteredActionListBodyLoader.displayName.
packages/react/src/FilteredActionList/FilteredActionListInput.tsxAdds FilteredActionListInput.displayName.
packages/react/src/Details/Details.tsxRenames Summary.displayName to Details.Summary.
packages/react/src/DataTable/Table.tsxAdds many displayName assignments for table subcomponents.
packages/react/src/DataTable/Pagination.tsxAdds Pagination.displayName.
packages/react/src/DataTable/ErrorDialog.tsxAdds ErrorDialog.displayName.
packages/react/src/Blankslate/Blankslate.tsxAdds Blankslate.* subcomponent displayNames.
packages/react/src/Banner/Banner.tsxRenames banner action displayNames to Banner.*.
packages/react/src/ActionList/TrailingAction.tsxAdds displayName aligned with slot marker.
packages/react/src/ActionList/Divider.tsxAdds displayName aligned with slot marker.
packages/react/src/ActionList/Description.tsxAdds displayName aligned with slot marker.
packages/react/src/ActionBar/ActionBar.tsxAdds displayNames for ActionBar compound exports.
.github/skills/display-name/SKILL.mdNew contributor skill documenting displayName conventions.
.github/copilot-instructions.mdLinks the new display-name skill for future reviews/authoring.
.changeset/displayname-consistency.mdPatch changeset describing the displayName normalization.

Copilot's findings

Comments suppressed due to low confidence (3)

packages/react/src/TooltipV2/Tooltip.tsx:403

  • Tooltip is a forwardRef wrapper around an anonymous arrow function and also has a __SLOT__ marker, but it never sets Tooltip.displayName. In DevTools this will typically show as ForwardRef/Tooltip inconsistently, and it also breaks the “displayName matches slot name” convention documented in this PR. Consider setting Tooltip.displayName = 'Tooltip' (and keeping it aligned with Symbol('Tooltip')).
 )
},
)
Tooltip.__SLOT__ = Symbol('Tooltip')

packages/react/src/DataTable/Pagination.tsx:390

  • Pagination is exposed as Table.Pagination in packages/react/src/DataTable/index.ts, but its displayName is set to DataTable.Pagination. This makes DevTools/error stacks inconsistent with the public API. Consider renaming to Table.Pagination (or aligning the compound export to match DataTable.*).

Pagination.displayName = 'DataTable.Pagination'

packages/react/src/DataTable/ErrorDialog.tsx:42

  • ErrorDialog is exposed as Table.ErrorDialog in packages/react/src/DataTable/index.ts, but its displayName is set to DataTable.ErrorDialog. This makes DevTools/error stacks inconsistent with the public API. Consider renaming to Table.ErrorDialog (or aligning the compound export to match DataTable.*).
}
ErrorDialog.displayName = 'DataTable.ErrorDialog'
  • Files reviewed: 30/30 changed files
  • Comments generated: 3

Comment on lines +412 to +424
Table.displayName = 'DataTable.Table'
TableHead.displayName = 'DataTable.Head'
TableBody.displayName = 'DataTable.Body'
TableHeader.displayName = 'DataTable.Header'
TableRow.displayName = 'DataTable.Row'
TableCell.displayName = 'DataTable.Cell'
TableCellPlaceholder.displayName = 'DataTable.CellPlaceholder'
TableContainer.displayName = 'DataTable.Container'
TableTitle.displayName = 'DataTable.Title'
TableSubtitle.displayName = 'DataTable.Subtitle'
TableDivider.displayName = 'DataTable.Divider'
TableActions.displayName = 'DataTable.Actions'
TableSkeleton.displayName = 'DataTable.Skeleton'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to the copilot comment


Tooltip.directions = ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw']

Tooltip.__SLOT__ = Symbol('DEPRECATED_Tooltip')
Comment on lines +42 to +48
```

### 3. Sub-components in the slot system

The slot system's dev-mode `displayName`-mismatch warning compares `child.type.displayName` against the slot component's `displayName`. Wrappers built with `asSlot` inherit the marker; setting an explicit `displayName` on each slot sub-component keeps the warning useful even when the wrapper's `Function.name` is generic.

## When you can skip `displayName`

@joshblackjoshblack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wanted to say 👍 I'm not sure what we'll do in the future if we offer both but for now this is definitely the convention. Is there a way to create an eslint rule for this, as well? Or would that be too complex?

Comment on lines +412 to +424
Table.displayName = 'DataTable.Table'
TableHead.displayName = 'DataTable.Head'
TableBody.displayName = 'DataTable.Body'
TableHeader.displayName = 'DataTable.Header'
TableRow.displayName = 'DataTable.Row'
TableCell.displayName = 'DataTable.Cell'
TableCellPlaceholder.displayName = 'DataTable.CellPlaceholder'
TableContainer.displayName = 'DataTable.Container'
TableTitle.displayName = 'DataTable.Title'
TableSubtitle.displayName = 'DataTable.Subtitle'
TableDivider.displayName = 'DataTable.Divider'
TableActions.displayName = 'DataTable.Actions'
TableSkeleton.displayName = 'DataTable.Skeleton'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to the copilot comment

@llastflowers
llastflowers removed their request for review July 21, 2026 16:29
@llastflowers

Copy link
Copy Markdown
Contributor

Removing myself as reviewer for now; feel free to add me again if needed when this is ready for re-review!

@lesliecdubs

Copy link
Copy Markdown
Member

Nudging here as it looks like this is starting to go stale with a few conflicts. @adierkens is it worth trying to get this over the line? If so and you don't have capacity, could we hand off to the FR to get it over the line?

@adierkens

Copy link
Copy Markdown
ContributorAuthor

Yeah let me take a pass at getting this back and ready

@adierkens
adierkens marked this pull request as draft July 22, 2026 18:09
adierkensand others added 2 commits July 22, 2026 14:11
- Renames sub-component displayName strings to the canonical
'Parent.Slot' convention (BannerPrimaryAction → Banner.PrimaryAction,
TimelineItem → Timeline.Item, ParentLink → PageHeader.ParentLink,
HorizontalDivider → PageLayout.HorizontalDivider, etc.).
- Adds displayName to compound sub-components where the runtime function
name doesn't match the canonical name (e.g. Visual → Blankslate.Visual,
SegmentedControlButton → SegmentedControl.Button, Panel → SelectPanel,
FormControlCaption → FormControl.Caption).
- Adds a contributor skill at .github/skills/display-name/SKILL.md
documenting when displayName is required vs redundant, the canonical
naming convention, and how it interacts with the slot system.
- Skips displayName additions for top-level components whose function or
variable name already matches the canonical name — modern React infers
it from Function.name and the variable assignment.
No runtime behaviour changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 29/29 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment threadpackages/react/src/DataTable/Table.tsx
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6c783ef1-0293-4b28-8e57-3104aeaa1520

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 29/29 changed files
  • Comments generated: 1
  • Review effort level: Low

ActionBarIconButton.displayName = 'ActionBar.IconButton'
ActionBarGroup.displayName = 'ActionBar.Group'
ActionBarMenu.displayName = 'ActionBar.Menu'
VerticalDivider.displayName = 'ActionBar.VerticalDivider'
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary ReleaseApply this label when you want CI to create a canary release of the current PRintegration-tests: recommendedThis change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@adierkens@llastflowers@lesliecdubs@joshblack