AntaExpander
Switch to dark theme
Search documentation
On this page

Expander Documentation has not yet been reviewed by a human.

Expander keeps a section title visible while its content opens and closes. Use it for supporting details, settings groups, or sections that readers can reveal as needed. Select the header or focus it and press Enter or Space to toggle.

Priority and tone

Choose primary for a pronounced surface, secondary for a subtle filled box, or tertiary for a transparent disclosure in a content column. tone colors the header and filled surfaces using the current theme.

PrimaryA pronounced section surface.SecondaryA subtle section surface.TertiaryA disclosure in a content column.
<Expander title="Primary" priority="primary" tone="brand"><Text>A pronounced section surface.</Text></Expander>
<Expander title="Secondary"><Text>A subtle section surface.</Text></Expander>
<Expander title="Tertiary" priority="tertiary"><Text>A disclosure in a content column.</Text></Expander>

Title and level

A string or number title uses Expander’s type scale. level changes that scale without adding heading semantics. Pass a Title or your own heading when the section belongs in the document outline; that node owns its typography.

Section titleA larger title scale.Supporting detailsA compact title scale.
<Expander title="Section title" level={2}><Text>A larger title scale.</Text></Expander>
<Expander title="Supporting details" level={6}><Text>A compact title scale.</Text></Expander>

Selecting a link, input, or enabled control in the title activates that control without toggling the section. Selecting title text keeps the selection. Prefer actions for independent header controls so they sit outside the toggle button.

Actions and indicators

Actions stay visible when the section is closed. Use them for operations that should remain available without opening the content. indicatorPlacement="end" places the disclosure mark after the actions. At the start, the chevron turns from right to down; at the end, it turns from down to up.

Choose triangle for a filled arrow or plus for a plus that becomes a minus. Custom artwork can rotate as one node or switch between closed and open visuals. Indicator artwork stays decorative and adds no focus stop.

The demonstration uses a custom title, two indicator visuals, and an independent Download action. Open the section to see the action count.

Build artifactsDownloadDownload requests: 0. The action leaves the disclosure state unchanged.
import { Button, Expander, Icon, Text, Title } from '@antadesign/anta'
import { useState } from 'react'
function ExpanderActionsDemo() {
const [downloads, setDownloads] = useState(0)
return (
<Expander
title={<Title level={4}>Build artifacts</Title>}
indicatorPlacement="end"
indicator={{ closed: <Icon shape="plus" />, open: <Icon shape="minus" /> }}
actions={<Button size="small" label="Download" onClick={() => setDownloads(count => count + 1)} />}
>
<Text>Download requests: {downloads}. The action leaves the disclosure state unchanged.</Text>
</Expander>
)
}

Round and outdent

Use round for a 1rem radius that stays constant when the section opens. Use outdent with priority="tertiary" to align the title and body with surrounding prose. The start indicator hangs in the gutter; an end indicator stays inside the end edge.

Rounded sectionThe corners keep their radius when expanded.Aligned sectionThe title and body align with the surrounding content.
<Expander title="Rounded section" round><Text>The corners keep their radius when expanded.</Text></Expander>
<Expander title="Aligned section" priority="tertiary" outdent defaultOpen><Text>The title and body align with the surrounding content.</Text></Expander>

Disabled

disabled removes the toggle button from focus navigation and prevents toggling. An open section stays open. Header actions remain available; disable those controls separately when needed.

Disabled, closedCollapsed content.Disabled, openContent remains visible.
<Expander title="Disabled, closed" disabled><Text>Collapsed content.</Text></Expander>
<Expander title="Disabled, open" disabled defaultOpen><Text>Content remains visible.</Text></Expander>

Open state

Without open, Expander owns its state. defaultOpen chooses its initial state. Closing keeps the content mounted and removes it from focus navigation and the accessibility tree. Reopening restores its visibility and interaction.

Call event.preventDefault() in onStateChange to reject an uncontrolled request synchronously. The demonstration keeps the section open until you clear the checkbox.

Keep details openUnsaved changesClear the checkbox to allow this section to close.
import { Box, Checkbox, Expander, Text } from '@antadesign/anta'
import { useState } from 'react'
function CancelableExample() {
const [keepOpen, setKeepOpen] = useState(true)
return (
<Box display="grid" gap={12} style={{ width: '100%' }}>
<Checkbox checked={keepOpen} onStateChange={(_, { next }) => setKeepOpen(next === true)}>Keep details open</Checkbox>
<Expander title="Unsaved changes" defaultOpen onStateChange={(event, { next }) => {
if (!next && keepOpen) event.preventDefault()
}}>
<Text>Clear the checkbox to allow this section to close.</Text>
</Expander>
</Box>
)
}

Pass open to control the section. Update it with detail.next in onStateChange to accept a request, or leave it unchanged to reject. The following demonstration counts requests even when they are rejected.

Accept state requestsRelease detailsThe content stays mounted when closed.State requests: 0Show details
import { Box, Button, Checkbox, Expander, Text } from '@antadesign/anta'
import { useState } from 'react'
function ControlledExample() {
const [open, setOpen] = useState(false)
const [accept, setAccept] = useState(true)
const [requests, setRequests] = useState(0)
return (
<Box display="grid" gap={12} style={{ width: '100%' }}>
<Checkbox checked={accept} onStateChange={(_, { next }) => setAccept(next === true)}>Accept state requests</Checkbox>
<Expander title="Release details" open={open} onStateChange={(_, { next }) => {
setRequests(count => count + 1)
if (accept) setOpen(next)
}}>
<Text>The content stays mounted when closed.</Text>
</Expander>
<Text>State requests: {requests}</Text>
<Button label="Show details" onClick={() => setOpen(true)} />
</Box>
)
}

Accordion

Share one open item identifier across a group of controlled Expanders when only one section should be open at a time.

Shipping & deliveryOrders ship within two business days; delivery takes three to five more.ReturnsUnworn items return free within 30 days of delivery.WarrantyEvery product carries a one-year limited warranty.
import { Box, Expander, Text } from '@antadesign/anta'
import { useState } from 'react'
const items = [
{ id: 'shipping', title: 'Shipping', body: 'Orders ship within two business days.' },
{ id: 'returns', title: 'Returns', body: 'Returns are accepted within 30 days.' },
]
function Accordion() {
const [openId, setOpenId] = useState('shipping')
return (
<Box display="grid" gap={1}>
{items.map(item => (
<Expander key={item.id} title={item.title} open={openId === item.id}
onStateChange={(_, { next }) => setOpenId(next ? item.id : null)}>
<Text>{item.body}</Text>
</Expander>
))}
</Box>
)
}

Component props

Prop Type Default Description
title ReactNode — Summary (header) content. A string is rendered with the level type scale; pass a node (e.g. a <Title>) for full control or real heading semantics in the document outline.
level? 123456 5 Heading type scale applied to a string title (mirrors <Title>'s levels). Visual only — for outline semantics, pass a <Title> as the title.
tone? neutralbrandcriticalinfosuccesswarningstring 'neutral' Semantic tone, or any literal CSS color ('#ff1493', 'rebeccapurple') for a one-off custom tone. Named tones re-point the text and (on filled priorities) the surface to the matching palette; a custom color keeps its hue while lightness/chroma are pinned. 'neutral' (the default) is the same as omitting it.
priority? primarysecondarytertiary 'secondary' Surface emphasis. secondary (the default) is a subtle fill; primary is a more pronounced card; tertiary is transparent (the bare disclosure).
outdent? boolean — Align the title and body with surrounding content on a tertiary expander. With the default start indicator, the mark hangs in the gutter; an end indicator stays inside its edge.
actions? ReactNode — Header actions rendered outside the toggle trigger. With an end indicator, actions sit between the title and indicator.
indicator? ReactNode— 'chevron' Built-in disclosure mark, a decorative node, or separate closed/open visuals. Other primitive values leave the indicator empty. Custom nodes are passive; use actions for controls.
indicatorPlacement? startend 'start' Place the disclosure mark before the title or after the actions.
disabled? boolean — Disables the header: not clickable or focusable, hover affordance off, text dimmed. The open state freezes as-is — disabling an open expander keeps it open. actions stay live; disable them separately if needed.
round? booleannumberstring — Use a 1rem corner radius in both folded and expanded states, independent of title level or header height. Pass a number (px) or CSS length string for a custom radius.
open? boolean — Controlled open state. When provided, the consumer owns open/close. Selecting the summary requests a change through onStateChange; omit open for uncontrolled use.
defaultOpen? boolean — Initial open state for the uncontrolled case.
onStateChange? (event, detail) => void — Fired before the open state changes. event is the cancelable statechange — call event.preventDefault() to veto an uncontrolled toggle (e.g. confirm before closing). detail.next is the requested open state, detail.prev the current one (booleans). In controlled mode, apply detail.next to open to accept, or do nothing to reject.
Inherited props (children, className, id, slot, style, tabIndex)
Prop Type Default Description
children? ReactNode — Child elements. When provided, replaces the component's default label/content.
className? string — CSS class on the component's root element (merged with the component's own classes). Use it directly for layout and positioning — grid/flex placement, margins, alignment — rather than wrapping the component in a <div>/<span>.
id? string — HTML id attribute.
slot? string — Assigns the element to a named <slot> of a parent web component (e.g. slot="header" inside a <Dialog>, slot="footer" inside a <Dialog>).
style? CSSProperties — Inline styles on the component's root element. Set layout/positioning here (or via className) directly on the component instead of adding a wrapper.
tabIndex? number — Tab order. Set to -1 to skip the element when tabbing.

Plus every standard DOM event handler (onClick, onFocus, onKeyDown, …) and any data-* or aria-* attribute — forwarded as-is to the underlying <a-*> element.

Web Component

Supply direct named regions. Use a-expander-summary for the built-in title scale, with level on that region. Use boxless a-expander-title for custom title markup. Put controls in a-expander-actions and body content in a-expander-details. Place a-expander-indicator before the title or after the actions to choose its position. Set its shape to chevron, triangle, or plus, or omit shape and supply custom artwork as children.

Build artifactsDownloadThe title and indicator toggle this section. Download is an independent action.
<a-expander default-state="open">
<a-expander-summary level="4">Build artifacts</a-expander-summary>
<a-expander-actions>
<a-button role="button" tabindex="0" size="small">
<a-button-label>Download</a-button-label>
</a-button>
</a-expander-actions>
<a-expander-indicator shape="chevron" aria-hidden="true" inert></a-expander-indicator>
<a-expander-details>The title and indicator toggle this section. Download is an independent action.</a-expander-details>
</a-expander>

The element retains a native button and inert collapse region in shadow DOM. It projects authored regions without changing their attributes, styles, ARIA, or children. The default styling loads from external CSS. Import @antadesign/anta/elements/a-expander for browser behavior; import the entries for any nested Anta controls separately. The full bundle includes them.

state="open" or state="closed" controls the section. Without state, default-state seeds it once; the omitted default is closed. Reconnection retains applied state. statechange is a cancelable, non-bubbling request with { next, prev } in the open/closed vocabulary. Call preventDefault() to reject an uncontrolled request. For controlled use, update state to accept or leave it unchanged to reject. The element owns the native button’s expanded and disabled states and the collapsed region’s inertness.

The same indicator element renders before and after registration. An omitted indicator renders no artwork. JSX creates the built-in chevron automatically; indicatorPlacement changes the region order.

One custom a-expander-indicator rotates with state. Two regions with data-when="closed" and data-when="open" switch instead. Raw authors mark all indicator artwork aria-hidden="true" and inert. The owned indicator container also excludes its projected artwork from interaction and accessibility.

Omit unused regions. CSS hides empty actions regions. Whitespace counts as content; JSX omits whitespace-only string actions. Mark an otherwise unrecognized interactive title widget data-expander-ignore to prevent toggling. A disabled title control behaves like title text; actions never toggle.

Named regions replace public slot attributes, and raw level moves from a-expander to a-expander-summary. Raw indicator shape and placement now belong to its region and DOM order. Existing shadow part overrides remain supported. JSX props stay the same.

Named tones and boolean round need no inline styles. Custom tones and radii use JSX-owned CSS variables. Under a CSP that blocks style attributes, supply --expander-tone-source or --expander-round through external CSS. Raw authors also use those variables on browsers without typed CSS attr().

Native details[data-anta] shares this component’s stylesheet and entry. Its summary and open attribute retain native behavior; it does not use Anta’s cancelable state contract. Keep its level attribute on details.

Styling

Use priority, tone, round, and outdent for the built-in appearance. --expander-tone-source connects a custom tone to an application color, and --expander-gutter sets the shared title/body inset. Style authored regions directly and the owned button, actions row, indicator, or content through ::part(). Unlayered rules override Anta’s defaults without !important.

The classes below identify demonstrations. Substitute your own selectors.

Hide the indicator and reduce the title/body inset.

A section without the built-in indicatorThe title and keyboard interaction remain available.
a-expander.hide-chevron > a-expander-indicator { display: none; }
a-expander.hide-chevron { --expander-gutter: 12px; }

Change header height and hover appearance while keeping its focus ring.

Hover or focus the headerThe button keeps native keyboard behavior.
a-expander.header-tint::part(summary) { min-height: 40px; }
a-expander.header-tint::part(summary):enabled:hover { background: var(--bg-3); }

Respond to applied state without managing CSS classes.

Open to highlight the indicatorThe indicator follows the applied state.
a-expander.state-indicator > a-expander-indicator { color: var(--text-4); }
a-expander.state-indicator:state(open) > a-expander-indicator { color: var(--text-1); }