react-collapse vs react-accessible-accordion vs react-collapsible
React Libraries for Accessible Collapsible Components
react-collapsereact-accessible-accordionreact-collapsible
React Libraries for Accessible Collapsible Components

react-accessible-accordion, react-collapse, and react-collapsible are React libraries designed to create collapsible UI components such as accordions, expandable panels, and toggleable sections. These packages help developers show or hide content based on user interaction while managing animation, state, and — in some cases — accessibility concerns. Each library takes a different approach: react-accessible-accordion emphasizes WAI-ARIA compliance and built-in keyboard navigation; react-collapse offers a minimal, unopinionated collapse primitive focused on smooth animation; and react-collapsible provides a simple, self-contained toggle component with basic styling and animation but limited accessibility support.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
react-collapse326,8891,13472.6 kB104 years agoMIT
react-accessible-accordion144,389790108 kB2910 months agoMIT
react-collapsible108,20554745.3 kB32-MIT

Building Accessible Accordions in React: react-accessible-accordion vs react-collapse vs react-collapsible

When you need to show and hide content in a UI — like FAQs, settings panels, or product details — collapsible components are essential. But not all implementations handle accessibility, performance, or developer experience equally. Let’s compare three popular React packages for this task: react-accessible-accordion, react-collapse, and react-collapsible.

🧩 Core Philosophy: Accessibility-First vs Minimalist vs Legacy Simplicity

react-accessible-accordion is built from the ground up with WAI-ARIA standards in mind. It automatically manages ARIA attributes (aria-expanded, aria-controls, etc.), keyboard navigation (arrow keys, Home/End), and focus management. If your team prioritizes inclusive design and compliance (e.g., WCAG), this is the only one of the three that enforces accessibility by default.

// react-accessible-accordion
import {
  Accordion,
  AccordionItem,
  AccordionItemHeading,
  AccordionItemButton,
  AccordionItemPanel
} from 'react-accessible-accordion';

<Accordion>
  <AccordionItem>
    <AccordionItemHeading>
      <AccordionItemButton>FAQ #1</AccordionItemButton>
    </AccordionItemHeading>
    <AccordionItemPanel>
      <p>Answer goes here.</p>
    </AccordionItemPanel>
  </AccordionItem>
</Accordion>

react-collapse takes a minimalist, unopinionated approach. It provides a single <Collapse> component that animates height transitions using CSS transforms and requestAnimationFrame. It doesn’t include any accordion logic — you must manage open/closed state yourself. This gives you full control but shifts the burden of accessibility and interaction patterns to you.

// react-collapse
import Collapse from 'react-collapse';

function MyCollapsible({ isOpen }) {
  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
      <Collapse isOpened={isOpen}>
        <div>Content here</div>
      </Collapse>
    </div>
  );
}

react-collapsible offers a simple, self-contained toggle component with basic animation. It bundles the trigger button and panel together, reducing boilerplate compared to react-collapse. However, it lacks built-in ARIA support and requires manual implementation of keyboard navigation and screen reader announcements.

// react-collapsible
import Collapsible from 'react-collapsible';

<Collapsible trigger="Click me">
  <p>Hidden content</p>
</Collapsible>

♿ Accessibility Support: Out-of-the-Box vs DIY

Only react-accessible-accordion ships with full WAI-ARIA compliance:

  • Auto-generated unique IDs linking buttons to panels via aria-controls
  • aria-expanded toggled automatically
  • Arrow key navigation between items
  • Focus trapped within open panels (optional)
  • Screen reader-friendly semantics

In contrast, react-collapse and react-collapsible provide zero accessibility features. You must manually:

  • Add role="button" or use real <button> elements
  • Manage aria-expanded, aria-controls, and id associations
  • Implement keyboard handlers for Enter, Space, and arrow keys
  • Ensure focus order remains logical

Example of what you’d need to add manually with react-collapsible:

// Manual ARIA with react-collapsible (not built-in)
const [isOpen, setIsOpen] = useState(false);
const panelId = 'panel-' + Math.random().toString(36).substr(2, 9);

<>
  <button
    aria-expanded={isOpen}
    aria-controls={panelId}
    onClick={() => setIsOpen(!isOpen)}
  >
    Toggle
  </button>
  {isOpen && (
    <div id={panelId} role="region">
      <p>Content</p>
    </div>
  )}
</>

This extra work is error-prone and often neglected in practice — making react-accessible-accordion the safer choice for production apps where accessibility is non-negotiable.

⚙️ Animation and Performance

All three libraries animate height changes, but they do it differently:

  • react-accessible-accordion: Uses CSS transitions on max-height. Smooth but can be less performant with very tall content since max-height must be set to an estimated maximum.

  • react-collapse: Measures content height dynamically and uses transform: scaleY() combined with height for smoother, GPU-accelerated animations. More complex internally but better for variable or dynamic content.

  • react-collapsible: Relies on CSS max-height transitions similar to the first option. Simpler but shares the same limitation with unknown content heights.

If you’re animating large or dynamic content (e.g., user-generated text), react-collapse’s measurement-based approach avoids layout thrashing and feels more responsive.

🔁 State Management: Built-In vs External

react-accessible-accordion includes internal state management. You can let it handle which panels are open, or override behavior via props like preExpanded and onChange. It supports both single and multi-open modes out of the box.

react-collapse is completely stateless. You pass isOpened={true/false} and handle all logic yourself. This integrates cleanly with Redux, Zustand, or any state system but adds wiring overhead.

react-collapsible manages its own open/closed state internally but allows limited control via open and onOpen/onClose props. However, it doesn’t support controlled mode as robustly as the others — trying to force it closed from outside can lead to sync issues.

🛑 Maintenance Status and Deprecation Warnings

As of 2024:

  • react-accessible-accordion is actively maintained, with recent updates addressing React 18 compatibility and TypeScript improvements.

  • react-collapse is stable and maintained, though updates are infrequent. It works reliably with modern React versions.

  • react-collapsible shows signs of stagnation. Its last meaningful update was years ago, and it hasn’t been adapted for concurrent rendering patterns. While not officially deprecated, it lacks modern React best practices and should be avoided in new projects unless you’re maintaining legacy code.

🧪 Real-World Usage Scenarios

Scenario 1: Public-Facing FAQ Page (Accessibility Required)

Choose react-accessible-accordion

  • Compliance with legal accessibility standards (ADA, Section 508)
  • Keyboard and screen reader users get a seamless experience
  • Minimal dev effort to meet requirements

Scenario 2: Internal Admin Dashboard (Animation Quality Matters)

Choose react-collapse

  • You control all state via global store
  • Need smooth animations for dynamic content
  • Accessibility is lower priority (but still recommended!)

Scenario 3: Quick Prototype or Low-Stakes Side Project

⚠️ Avoid react-collapsible — even for prototypes, prefer react-accessible-accordion or react-collapse. The small convenience isn’t worth technical debt or accessibility gaps.

📊 Summary Table

Featurereact-accessible-accordionreact-collapsereact-collapsible
ARIA Compliance✅ Full, automatic❌ None❌ None
Keyboard Navigation✅ Built-in❌ Manual❌ Manual
Animation TechniqueCSS max-heightDynamic height + transformCSS max-height
State Management✅ Internal + controlled options❌ Fully external⚠️ Partial internal
Multi-Panel Support✅ Yes✅ (via your logic)✅ Yes
Maintenance Status✅ Active✅ Stable⚠️ Stale / Not recommended

💡 Final Recommendation

  • Default choice: react-accessible-accordion — it’s the only one that gets accessibility right without extra work.
  • Specialized use case: react-collapse — if you need pixel-perfect animations and already manage state externally.
  • Avoid: react-collapsible — outdated, incomplete accessibility, and no compelling advantage over the others.

Remember: accessible components aren’t just about compliance — they make your UI more robust, testable, and usable for everyone. Start with the right foundation.

How to Choose: react-collapse vs react-accessible-accordion vs react-collapsible
  • react-collapse:

    Choose react-collapse when you need fine-grained control over animation performance and already manage component state externally (e.g., via Redux or context). It provides a lightweight, unstyled collapse primitive that works well for dashboards or internal tools where you can implement accessibility manually — but only if your team has the bandwidth to do so correctly.

  • react-accessible-accordion:

    Choose react-accessible-accordion when building public-facing applications where accessibility compliance (WCAG, ADA) is required. It handles ARIA attributes, keyboard navigation, and focus management automatically, reducing the risk of accessibility bugs. Ideal for FAQs, documentation sites, or any interface that must work well with screen readers and keyboards without extra engineering effort.

  • react-collapsible:

    Avoid react-collapsible in new projects. While it offers a quick setup with minimal code, it lacks built-in accessibility features, hasn’t been actively maintained, and doesn’t follow modern React patterns. It may seem convenient for prototypes, but the technical debt and accessibility gaps outweigh its simplicity.

README for react-collapse

react-collapse npm

Gitter

CircleCI Dependencies Dev Dependencies

Component-wrapper for collapse animation for elements with variable (and dynamic) height

React Collapse

Demo

http://nkbt.github.io/react-collapse

Codepen demo

http://codepen.io/nkbt/pen/MarzEg

Installation

NPM

npm install --save react-collapse

yarn

yarn add react-collapse

1998 Script Tag:

<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-collapse/build/react-collapse.min.js"></script>
(Module exposed as `ReactCollapse`)

Usage

1. Content is always mounted (default)

import {Collapse} from 'react-collapse';

// ...
<Collapse isOpened={true || false}>
  <div>Random content</div>
</Collapse>

2. Content unmounts when collapsed

Use Unmount component provided as:

import {UnmountClosed} from 'react-collapse';

// ...
<UnmountClosed isOpened={true || false}>
  <div>Random content</div>
</UnmountClosed>

Example example/App/AutoUnmount.js


3. Controlled and accessible

If you want a controlled and accessible implementation, check out this example

Options

isOpened: PropTypes.boolean.isRequired

Expands or collapses content.

children: PropTypes.node.isRequired

One or multiple children with static, variable or dynamic height.

<Collapse isOpened={true}>
  <p>Paragraph of text</p>
  <p>Another paragraph is also OK</p>
  <p>Images and any other content are ok too</p>
  <img src="nyancat.gif" />
</Collapse>

theme: PropTypes.objectOf(PropTypes.string)

It is possible to set className for extra div elements that ReactCollapse creates.

Example:

<Collapse theme={{collapse: 'foo', content: 'bar'}}>
  <div>Customly animated container</div>
</Collapse>

Default values:

const theme = {
  collapse: 'ReactCollapse--collapse',
  content: 'ReactCollapse--content'
}

Which ends up in the following markup:

<div class="ReactCollapse--collapse">
  <div class="ReactCollapse--content">
    {children}
  </div>
</div>

IMPORTANT: these are not style objects, but class names!

onRest, onWork: PropTypes.func

Callback functions, triggered when animation has completed (onRest) or has just started (onWork)

Both functions are called with argument:

const arg = {
  isFullyOpened: true || false, // `true` only when Collapse reached final height
  isFullyClosed: true || false, // `true` only when Collapse is fully closed and height is zero
  isOpened: true || false, // `true` if Collapse has any non-zero height
  containerHeight: 123, // current pixel height of Collapse container (changes until reaches `contentHeight`)
  contentHeight: 123 // determined height of supplied Content
}
<Collapse onRest={console.log} onWork={console.log}>
  <div>Container text</div>
</Collapse>

Example example/App/Hooks.js

initialStyle: PropTypes.shape

initialStyle: PropTypes.shape({
  height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  overflow: PropTypes.string
})

You may control initial element style, for example to force initial animation from 0 to height by using initialStyle={{height: '0px', overflow: 'hidden'}}

IMPORTANT Any updates to this prop will be ignored after first render. Default value is determined based on initial isOpened value:

  initialStyle = props.isOpened
    ? {height: 'auto', overflow: 'initial'}
    : {height: '0px', overflow: 'hidden'};

Example: example/App/ForceInitialAnimation.js

checkTimeout: PropTypes.number

Number in ms.

Collapse will check height after thins timeout to determine if animation is completed, the shorter the number - the faster onRest will be triggered and the quicker hight: auto will be applied. The downside - more calculations. Default value is: 50.

Pass-through props

IMPORTANT Collapse does not support any pass-through props, so any non-supported props will be ignored

Because we need to have control over when Collapse component is updated and it is not possible or very hard to achieve when any random props can be passed to the component.

Behaviour notes

  • initially opened Collapse elements will be statically rendered with no animation. You can override this behaviour by using initialStyle prop

  • due to the complexity of margins and their potentially collapsible nature, ReactCollapse does not support (vertical) margins on their children. It might lead to the animation "jumping" to its correct height at the end of expanding. To avoid this, use padding instead of margin. See #101 for more details

Migrating from v4 to v5

v5 was another complete rewrite that happened quite a while ago, it was published as @nkbt/react-collapse and tested in real projects for a long time and now fully ready to be used.

In the most common scenario upgrade is trivial (add CSS transition to collapse element), but if you were using a few deprecated props - there might be some extra work required.

Luckily almost every deprecated callback or prop has fully working analogue in v5. Unfortunately not all of them could maintain full backward compatibility, so please check this migration guide below.

1. Change in behaviour

New Collapse does not implement animations anymore, it only determines Content height and updates Collapse wrapper height to match it. Only after Collapse height reaches Content height (animation finished), Collapse's style is updated to have height: auto; overflow: initial.

The implications is that you will need to update your CSS with transition:

.ReactCollapse--collapse {
  transition: height 500ms;
}

IMPORTANT: without adding css transition there will be no animation and component will open/close instantly.

2. New props or updated props

  • onRest/onWork callbacks (see above for full description). Though onRest did exist previously, now it is called with arguments containing few operational params and flags.

  • initialStyle you may control initial element style, for example to force initial animation from 0 to height by using initialStyle={{height: '0px', overflow: 'hidden'}} IMPORTANT Any updates to this prop will be ignored after first render. Default value is:

      initialStyle = props.isOpened
        ? {height: 'auto', overflow: 'initial'}
        : {height: '0px', overflow: 'hidden'};
    
  • checkTimeout number in ms. Collapse will check height after thins timeout to determine if animation is completed, the shorter the number - the faster onRest will be triggered and the quicker hight: auto will be applied. The downside - more calculations. Default value is: 50.

3. Deprecated props (not available in v5)

  • Pass-through props - any unknown props passed to Collapse component will be ignored

  • hasNestedCollapse - no longer necessary, as v5 does not care if there are nested Collapse elements, see example/App/Nested.js

  • fixedHeight - no longer necessary, just set whatever height you need for content element and Collapse will work as expected, see example/App/VariableHeight.js

  • springConfig - as new Collapse relies on simple CSS transitions (or your own implementation of animation) and does not use react-collapse, springConfig is no longer necessary. You can control control animation with css like

    .ReactCollapse--collapse {
      transition: height 500ms;
    }
    
  • forceInitialAnimation - you can use new prop initialStyle={{height: '0px', overflow: 'hidden'}} instead, so when new height will be set after component is rendered - it should naturally animate.

  • onMeasure - please use onRest and onWork instead and pick contentHeight from argument

    <Collapse
      onRest={({contentHeight}) => console.log(contentHeight)}
      onWork={({contentHeight}) => console.log(contentHeight)}>
      <div>content</div>
    </Collapse>
    
  • onRender - since animations are fully controlled by external app (e.g. with CSS) we no draw each frame and do not actually re-render component anymore, so it is impossible to have onRender callback

Development and testing

Currently is being developed and tested with the latest stable Node on OSX.

To run example covering all ReactCollapse features, use yarn start, which will compile example/Example.js

git clone git@github.com:nkbt/react-collapse.git
cd react-collapse
yarn install
yarn start

# then
open http://localhost:8080

Tests

# to run ESLint check
yarn lint

# to run tests
yarn test

License

MIT