Skip to content

Expandable section

Opens and closes a titled section of a page from its gray title bar. Closed content stays mounted, hidden.

Edge Communications is replacing the backup generators at its three Austin sites before the summer storm season.

Installation

pnpm dlx shadcn@latest add @wisty/expandable-section

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • A page's content falls into titled sections people fold away one by one, like a record's Description Information and System Information.
  • The closed content must stay on the page, like form fields that still save, or state that survives closing.
  • A section of a Lightning page with a gray title bar and a chevron, as a record page's sections are.

Don't use it

  • Opening one section should close the others. Use Accordion.
  • The trigger isn't a title bar, like a "Show more" link or an icon button on a row. Use Collapsible and style the trigger.
  • A record's fields in the page layout's sections. Use Record detail, which draws its sections with this.
  • The content never folds. Use a heading.

Usage

import {
  ExpandableSection,
  ExpandableSectionContent,
  ExpandableSectionTrigger,
} from "@/components/wisty/expandable-section"
ExpandableSection
├── ExpandableSectionTrigger
└── ExpandableSectionContent
<ExpandableSection defaultOpen>
  <ExpandableSectionTrigger>Description Information</ExpandableSectionTrigger>
  <ExpandableSectionContent>…</ExpandableSectionContent>
</ExpandableSection>

Sections start closed; pass defaultOpen to start one open, or open and onOpenChange to control it. headingLevel on the trigger sets the heading around the title button.

API reference

PartBuilt onDescription
ExpandableSection<div> (opens in a new tab)A titled section of a page that people open and close from its title.
ExpandableSectionTrigger<button> (opens in a new tab)The section's title: a heading holding the button that opens and closes it.
ExpandableSectionContent<div> (opens in a new tab)What the section holds. It stays mounted while closed, hidden.

ExpandableSection

A titled section of a page that people open and close from its title.

PropTypeDefaultDescription
openbooleanWhether the section is open, controlled.
defaultOpenbooleanfalseWhether the section starts open, uncontrolled. Default false.
onOpenChange(open: boolean) => voidCalled when the title opens or closes the section.

ExpandableSectionTrigger

The section's title: a heading holding the button that opens and closes it.

PropTypeDefaultDescription
headingLevel2 | 3 | 4 | 5 | 62The level of the heading around the button. Default 2.

SLDS mapping

ExpandableSection is slds-section (slds-is-open while open), ExpandableSectionTrigger is the slds-section__title heading holding the slds-section__title-action button with its slds-section__title-action-icon chevron and the title in slds-truncate, and ExpandableSectionContent is slds-section__content.

Known gaps:

  • The chevron is lucide's ChevronDownIcon. SLDS uses its utility:switch caret.
  • No height animation: SLDS shows and hides the content at once, and so does this.

Keyboard interactions

Key Action
Tab Moves focus to the title button.
Enter or Space Opens or closes the section.

Accessibility

  • Set headingLevel to the level after the page's last heading, so each section shows in the outline. It's 2 by default, under a page's h1.
  • The title button reports aria-expanded and names the content it controls with aria-controls.
  • Closed content is hidden, so screen readers and find-in-page skip it until the section opens.

Rules

  • Keep the title to a few words: it truncates on one line.
  • Put the content inside ExpandableSectionContent; anything else in the section stays visible when it closes.