Skip to content

feat: normalise component displayName conventions#7871

Open
adierkens wants to merge 2 commits into
mainfrom
adierkens/displayname-consistency
Open

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

Conversation

@adierkens
Copy link
Copy Markdown
Contributor

@adierkens adierkens commented May 22, 2026

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

- 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>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 22, 2026

🦋 Changeset detected

Latest commit: 5ba1782

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

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

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-actions github-actions Bot 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.

@adierkens adierkens added the Canary Release Apply this label when you want CI to create a canary release of the current PR label May 22, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot requested a deployment to storybook-preview-7871 May 22, 2026 19:39 Abandoned
@adierkens adierkens marked this pull request as ready for review May 22, 2026 20:13
Copilot AI review requested due to automatic review settings May 22, 2026 20:13
@adierkens adierkens requested a review from a team as a code owner May 22, 2026 20:13
@adierkens adierkens requested a review from llastflowers May 22, 2026 20:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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
File Description
packages/react/src/TopicTag/TopicTagGroup.tsx Sets TopicTagGroup.displayName to TopicTag.Group.
packages/react/src/TooltipV2/Tooltip.tsx Minor formatting change near slot marker.
packages/react/src/Tooltip/Tooltip.tsx Minor formatting change near deprecated slot marker.
packages/react/src/Timeline/Timeline.tsx Renames displayName strings to Timeline.* dot notation.
packages/react/src/Stack/Stack.tsx Adds StackItem.displayName = 'Stack.Item'.
packages/react/src/SelectPanel/SelectPanelMessage.tsx Adds SelectPanelMessage.displayName = 'SelectPanel.Message'.
packages/react/src/SelectPanel/SelectPanel.tsx Sets root Panel.displayName = 'SelectPanel' for the exported SelectPanel.
packages/react/src/SegmentedControl/SegmentedControlIconButton.tsx Adds displayName aligned with slot marker.
packages/react/src/SegmentedControl/SegmentedControlButton.tsx Adds displayName aligned with slot marker.
packages/react/src/PageLayout/PageLayout.tsx Renames divider displayName strings to PageLayout.* dot notation.
packages/react/src/PageHeader/PageHeader.tsx Renames slot-ish subcomponent displayName strings to PageHeader.*.
packages/react/src/Pagehead/Pagehead.tsx Formatting-only change (blank line).
packages/react/src/FormControl/FormControlLeadingVisual.tsx Adds displayName aligned with slot marker.
packages/react/src/FormControl/FormControlLabel.tsx Adds displayName aligned with slot marker.
packages/react/src/FormControl/FormControlCaption.tsx Adds displayName aligned with slot marker.
packages/react/src/FilteredActionList/FilteredActionListLoaders.tsx Adds FilteredActionListBodyLoader.displayName.
packages/react/src/FilteredActionList/FilteredActionListInput.tsx Adds FilteredActionListInput.displayName.
packages/react/src/Details/Details.tsx Renames Summary.displayName to Details.Summary.
packages/react/src/DataTable/Table.tsx Adds many displayName assignments for table subcomponents.
packages/react/src/DataTable/Pagination.tsx Adds Pagination.displayName.
packages/react/src/DataTable/ErrorDialog.tsx Adds ErrorDialog.displayName.
packages/react/src/Blankslate/Blankslate.tsx Adds Blankslate.* subcomponent displayNames.
packages/react/src/Banner/Banner.tsx Renames banner action displayNames to Banner.*.
packages/react/src/ActionList/TrailingAction.tsx Adds displayName aligned with slot marker.
packages/react/src/ActionList/Divider.tsx Adds displayName aligned with slot marker.
packages/react/src/ActionList/Description.tsx Adds displayName aligned with slot marker.
packages/react/src/ActionBar/ActionBar.tsx Adds displayNames for ActionBar compound exports.
.github/skills/display-name/SKILL.md New contributor skill documenting displayName conventions.
.github/copilot-instructions.md Links the new display-name skill for future reviews/authoring.
.changeset/displayname-consistency.md Patch 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'

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`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary Release Apply this label when you want CI to create a canary release of the current PR integration-tests: recommended This 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.

2 participants