Components
Collapsible

Collapsible

An interactive component that can be expanded or collapsed.

Loading...

Animation

You can use CSS animations to create smooth transitions for opening and closing the Collapsible content. Utilize the data-state attribute in combination with the --height CSS variable to animate the open and closed states.

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--height);
  }
}

@keyframes slideUp {
  from {
    height: var(--height);
  }
  to {
    height: 0;
  }
}

[data-scope='collapsible'][data-part='content'][data-state='open'] {
  animation: slideDown 250ms;
}

[data-scope='collapsible'][data-part='content'][data-state='closed'] {
  animation: slideUp 200ms;
}

Examples

Learn how to use the Collapsible component in your project. Let's examine the most basic example

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const Basic = () => (
  <Collapsible.Root>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Disabled

Use the disabled prop to disable the collapsible and prevent it from being toggled.

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const Disabled = () => (
  <Collapsible.Root disabled>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Partial Collapse

Use the collapsedHeight or collapsedWidth props to create a "show more/less" pattern. When set, the content maintains the specified dimensions when collapsed instead of collapsing to 0px.

We expose the --collapsed-height or --collapsed-width variables to use in your CSS animations.

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const PartialCollapse = () => (
  <Collapsible.Root collapsedHeight="100px">
    <Collapsible.Trigger>
      Show More
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>
      <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat.
      </p>
      <p>
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
        sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
      </p>
      <p>
        Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem
        aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
      </p>
    </Collapsible.Content>
  </Collapsible.Root>
)

Interactive elements (links, buttons, inputs) within the collapsed area automatically become inert to prevent keyboard navigation to hidden content.

Nested Collapsibles

You can nest collapsibles within collapsibles to create hierarchical content structures.

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const NestedCollapsible = () => (
  <Collapsible.Root>
    <Collapsible.Trigger>
      Getting Started
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>
      <p>Welcome to the documentation. Here are some topics to explore:</p>

      <Collapsible.Root>
        <Collapsible.Trigger>
          Installation
          <Collapsible.Indicator>
            <ChevronRightIcon />
          </Collapsible.Indicator>
        </Collapsible.Trigger>
        <Collapsible.Content>
          <p>To install the package, run one of the following commands:</p>
          <p>npm install @ark-ui/react</p>
        </Collapsible.Content>
      </Collapsible.Root>

      <Collapsible.Root>
        <Collapsible.Trigger>
          Configuration
          <Collapsible.Indicator>
            <ChevronRightIcon />
          </Collapsible.Indicator>
        </Collapsible.Trigger>
        <Collapsible.Content>
          <p>Configure your project settings to use Ark UI components.</p>
        </Collapsible.Content>
      </Collapsible.Root>
    </Collapsible.Content>
  </Collapsible.Root>
)

Events

Use onExitComplete callback to listen for when the Collapsible.Content is no longer visible

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const OnExitComplete = () => (
  <Collapsible.Root onExitComplete={() => alert('on exit')}>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Lazy Mount

To delay the mounting of the Collapsible.Content, use the lazyMount prop

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const LazyMount = () => (
  <Collapsible.Root lazyMount>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Unmount on Exit

To remove the Collapsible.Content from the DOM when it is not visible, use the unmountOnExit prop

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const UnmountOnExit = () => (
  <Collapsible.Root unmountOnExit>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Lazy Mount + Unmount on Exit

Both lazyMount and unmountOnExit can be combined to ensure that the component is mounted only when the Collapsible is expanded and unmounted when it is collapsed:

import { Collapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const LazyMountAndUnmountOnExit = () => (
  <Collapsible.Root lazyMount unmountOnExit>
    <Collapsible.Trigger>
      Toggle
      <Collapsible.Indicator>
        <ChevronRightIcon />
      </Collapsible.Indicator>
    </Collapsible.Trigger>
    <Collapsible.Content>Content</Collapsible.Content>
  </Collapsible.Root>
)

Root Provider

Use the useCollapsible hook to create the collapsible store and pass it to the Collapsible.RootProvider component. This allows you to have maximum control over the collapsible programmatically.

import { Collapsible, useCollapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const RootProvider = () => {
  const collapsible = useCollapsible()

  return (
    <>
      <span>{collapsible.visible ? 'Visible' : 'Hidden'}</span>

      <Collapsible.RootProvider value={collapsible}>
        <Collapsible.Trigger>
          Toggle
          <Collapsible.Indicator>
            <ChevronRightIcon />
          </Collapsible.Indicator>
        </Collapsible.Trigger>
        <Collapsible.Content>Content</Collapsible.Content>
      </Collapsible.RootProvider>
    </>
  )
}

If you're using the Collapsible.RootProvider component, you don't need to use the Collapsible.Root component.

Programmatic Control

Use the useCollapsible hook with Collapsible.RootProvider to programmatically control the collapsible using the setOpen() method and read state properties like open and visible.

import { Collapsible, useCollapsible } from '@ark-ui/react/collapsible'
import { ChevronRightIcon } from 'lucide-react'

export const ProgrammaticOpen = () => {
  const collapsible = useCollapsible()

  return (
    <>
      <div>
        <p>
          Open: <strong>{String(collapsible.open)}</strong>
        </p>
        <p>
          Visible: <strong>{String(collapsible.visible)}</strong>
        </p>
      </div>

      <div style={{ display: 'flex', gap: '8px', marginTop: '8px' }}>
        <button type="button" onClick={() => collapsible.setOpen(true)}>
          Open
        </button>
        <button type="button" onClick={() => collapsible.setOpen(false)}>
          Close
        </button>
      </div>

      <Collapsible.RootProvider value={collapsible}>
        <Collapsible.Trigger>
          Toggle
          <Collapsible.Indicator>
            <ChevronRightIcon />
          </Collapsible.Indicator>
        </Collapsible.Trigger>
        <Collapsible.Content>Content</Collapsible.Content>
      </Collapsible.RootProvider>
    </>
  )
}

Guides

Animating the Indicator

To rotate the indicator icon (such as a chevron) when the collapsible opens and closes, use CSS transforms with the data-state attribute:

[data-scope='collapsible'][data-part='indicator'] {
  transition: transform 200ms;

  &[data-state='open'] {
    transform: rotate(180deg);
  }
}

open vs visible

When using useCollapsible or useCollapsibleContext, you can access the open and visible state properties. They seem similar but serve different purposes:

  • open: Indicates the intended state of the collapsible. This is true when the collapsible should be expanded and false when it should be collapsed. This changes immediately when triggered.

  • visible: Indicates whether the content is currently visible in the DOM. This accounts for exit animations - the content remains visible while the closing animation plays, even though open is already false. Once the animation completes, visible becomes false.

Animating the Content

Use the --height and/or --width CSS variables to animate the size of the content when it expands or closes.

If you use collapsedHeight or collapsedWidth, update your CSS animations to use the --collapsed-height or --collapsed-width variables as the starting/ending point:

@keyframes expand {
  from {
    height: var(--collapsed-height, 0);
  }
  to {
    height: var(--height);
  }
}

@keyframes collapse {
  from {
    height: var(--height);
  }
  to {
    height: var(--collapsed-height, 0);
  }
}

[data-scope='collapsible'][data-part='content'] {
  &[data-state='open'] {
    animation: expand 250ms;
  }
  &[data-state='closed'] {
    animation: collapse 250ms;
  }
}

API Reference

Root

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
defaultOpen
boolean

The initial open state of the collapsible when rendered. Use when you don't need to control the open state of the collapsible.

disabled
boolean

Whether the collapsible is disabled.

ids
Partial<{ root: string; content: string; trigger: string }>

The ids of the elements in the collapsible. Useful for composition.

lazyMountfalse
boolean

Whether to enable lazy mounting

onExitComplete
VoidFunction

The callback invoked when the exit animation completes.

onOpenChange
(details: OpenChangeDetails) => void

The callback invoked when the open state changes.

open
boolean

The controlled open state of the collapsible.

unmountOnExitfalse
boolean

Whether to unmount on exit.

Data AttributeValue
[data-scope]collapsible
[data-part]root
[data-state]"open" | "closed"

Content

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
CSS VariableDescription
--heightThe height of the element
--widthThe width of the element
Data AttributeValue
[data-scope]collapsible
[data-part]content
[data-collapsible]
[data-state]"open" | "closed"
[data-disabled]Present when disabled

Indicator

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]collapsible
[data-part]indicator
[data-state]"open" | "closed"
[data-disabled]Present when disabled

RootProvider

PropDefaultType
value
UseCollapsibleReturn

asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.

Trigger

PropDefaultType
asChild
boolean

Use the provided child element as the default rendered element, combining their props and behavior.

For more details, read our Composition guide.
Data AttributeValue
[data-scope]collapsible
[data-part]trigger
[data-state]"open" | "closed"
[data-disabled]Present when disabled

Accessibility

Keyboard Support

KeyDescription
Space
Opens/closes the collapsible.
Enter
Opens/closes the collapsible.