@collapsed/react
v5.0.0
Published
A React custom-hook for creating flexible and accessible expand/collapse components.
Downloads
2,413
Maintainers
Readme
NOTE
You're probably looking for react-collapsed. This package (alongside @collapsed/core) is a WIP rewrite to create a Vanilla JS core.
@collapsed/react (useCollapse)
A React hook for creating accessible expand/collapse components. Animates the height using CSS transitions from 0
to auto
.
Features
- Handles the height of animations of your elements,
auto
included! - You control the UI -
useCollapse
provides the necessary props, you control the styles and the elements. - Accessible out of the box - no need to worry if your collapse/expand component is accessible, since this takes care of it for you!
- No animation framework required! Simply powered by CSS animations
- Written in TypeScript
Demo
Installation
$ npm i @collapsed/react
Usage
Simple Usage
import React from "react";
import { useCollapse } from "@collapsed/react";
function Demo() {
const { getCollapseProps, getToggleProps, isExpanded } = useCollapse();
return (
<div>
<button {...getToggleProps()}>
{isExpanded ? "Collapse" : "Expand"}
</button>
<section {...getCollapseProps()}>Collapsed content 🙈</section>
</div>
);
}
Control it yourself
import React, { useState } from "react";
import { useCollapse } from "@collapsed/react";
function Demo() {
const [isExpanded, setExpanded] = useState(false);
const { getCollapseProps, getToggleProps } = useCollapse({ isExpanded });
return (
<div>
<button
{...getToggleProps({
onClick: () => setExpanded((prevExpanded) => !prevExpanded),
})}
>
{isExpanded ? "Collapse" : "Expand"}
</button>
<section {...getCollapseProps()}>Collapsed content 🙈</section>
</div>
);
}
API
useCollapse
takes the following options:
interface UseCollapseOptions {
/** If true, the disclosure is expanded. */
isExpanded?: boolean;
/**
* If true, the disclosure is expanded when it initially mounts.
* @default false
*/
defaultExpanded?: boolean;
/** Handler called when the disclosure expands or collapses */
onExpandedChange?: (state: boolean) => void;
/** Handler called at each stage of the animation. */
onTransitionStateChange?: (
state:
| "expandStart"
| "expanding"
| "expandEnd"
| "collapseStart"
| "collapsing"
| "collapseEnd",
) => void;
/** Timing function for the transition */
easing?: string;
/**
* Duration of the expand/collapse animation.
* If 'auto', the duration will be calculated based on the height of the collapse element
*/
duration?: "auto" | number;
/** Height in pixels that the collapse element collapses to */
collapsedHeight?: number;
/**
* Unique identifier used to for associating elements appropriately for accessibility.
*/
id?: string;
}
And returns the following API:
interface CollapseAPI {
isExpanded: boolean;
setExpanded: (update: boolean | ((prev: boolean) => boolean)) => void;
getToggleProps: <T extends HTMLElement>(
props?: React.ComponentPropsWithoutRef<T> & { refKey?: string },
) => React.ComponentPropsWithRef<T>;
getCollapseProps: <T extends HTMLElement>(
props?: React.ComponentPropsWithoutRef<T> & { refKey?: string },
) => React.ComponentPropsWithRef<T>;
}
Alternative Solutions
- react-spring - JavaScript animation based library that can potentially have smoother animations. Requires a bit more work to create an accessible collapse component.
- react-animate-height - Another library that uses CSS transitions to animate to any height. It provides components, not a hook.
FAQ
The collapse works by manipulating the height
property. If an element has vertical padding, that padding expandes the size of the element, even if it has height: 0; overflow: hidden
.
To avoid this, simply move that padding from the element to an element directly nested within in.
// from
<div {...getCollapseProps({style: {padding: 20}})}
This will do weird things
</div>
// to
<div {...getCollapseProps()}
<div style={{padding: 20}}>
Much better!
</div>
</div>