First developed and used by Aitenders
Install react-side-pane
npm i react-side-pane
yarn add react-side-pane
And import it inside your components via
⚠️ Breaking change:
As of v2.0.0, you can import SidePane only via its named export (as below).
Prior to v2.0.0, please use its default exportimport SidePane from "react-side-pane";
import { SidePane } from "react-side-pane";
react-side-pane is using React Portals under the hood and React transition group to handle the transitions. The SidePane should take all the screen. It can accept only one child (either a function or an element/component/dom node).
The pane only appears from the right.. it is really a react-right-side-pane at the moment. Also, react-side-pane offers only a few props to customize the pane so if you need any additionnal tweaks, feel free to open an issue or to contribute.
<SidePane open={open} width={50} onClose={handleClose}>
<SomeComponent /> // or some function {() => <>Hello world!</>}
</SidePane>
<SidePane open={open} width={50} onClose={handleClose}>
{/* Assuming SomeComponent calls a SidePane */}
{({ onActive }) => <SomeComponentWithASidePane onActive={onActive} />}
</SidePane>
or
// SomeComponent.js
export default function SomeComponent({ someComponentProps, onActive }) {
// onActive --> callback received from SidePane
... // Handle its SidePane open/close state
return (
<>
{...}
<SidePane open={open} width={50} onActive={onActive} onClose={handleClose}>
<span>Hello world!</span>
</SidePane>
</>
);
}
// Elsewhere
<SidePane open={open} width={50} onClose={handleClose}>
<SomeComponent someComponentProps={...} />
</SidePane>
The autoWidth
prop will override width
by taking the width of the SidePane's content via ref. This ref is passed down from SidePane to its content via forwarding:
const SidePaneContent = React.forwardRef(({ onActive }, ref) => {
// onActive callback to pass to an inner SidePane (see example above)
return (
<div ref={ref} style={{ width: "250px" }}>
{...}
</div>
);
});
Prop | Description | Default |
---|---|---|
appNodeId | DOM node id that contains the application (for aria-hidden) | "root" |
aria-describedby | aria-describedby | "" |
aria-label | aria-label | "side pane" |
aria-labelledby | aria-labelledby | "" |
autoWidth | Will take the width bounding box's width of the SidePane's child instead of width |
false |
backdropClassName | Classname to pass to the backdrop | "" |
backdropStyle | Style object to pass to the backdrop | {} |
children |
One React element or a function that can hold the onActive callback | (required) |
className | Classname to pass to the pane | "" |
containerId | You can specify an element ID to receive the side panes (portal). containerId will be passed to document.getElementById. If not filled, the side panes will portal to document.body. See usage below | "" |
disableBackdropClick | Prevents click on backdrop to trigger onClose | false |
disableEscapeKeyDown | Prevents Escape key down to trigger onClose. Recommended: Should not be true as it is part of a11y specs. | false |
disableRestoreFocus | Prevents restoring focus on previous active element after closing. Recommended: Should not be true as it is part of a11y specs. | false |
duration | Animation dur. (ms). Aniamtions are diabled when reduce-motion is on | 250 (ms) |
hideBackdrop | Makes the backdrop transparent | false |
offset | Space (width in %) between parent and child when both are open | 10 (%) |
onActive | Callback from child to parent to pass on the child width on open | null |
onClose |
Callback triggered on Escape or click on backdrop | (required) |
open |
Whether to display the pane | false (required) |
position | Side in which pane should be displayed "right" or "left" | "right" |
style | Style object to pass to the pane | {} |
width | Width of the pane in percentage. Max: 100. | 0 (%) |
Notes: Noticed while using MUI dialog with SidePane
MUI Dialog and SidePane are rendering by default in document.body using Portals. But using both at the same time triggered Maximum call stack size exceeded. at tryFocus
from one of our old dependency. We removed said dependency to use react-focus-lock instead which resolved the issue but popped a new one:
FocusLock: focus-fighting detected. Only one focus management system could be active.
To fix this, we are using shards
and whiteList
props from react-focus-lock. If you stumble upon this one in your console, consider the fix below and if it does not work, open an issue.
<body>
<div id="root"></div> <!-- your react app container -->
<div id="container"></div> <!-- the SidePane container (instead of document.body) -->
<!-- other stuff ... -->
<!-- other Modals/Dialogs etc... -->
</body>
function App() {
const [open, dispatchOpen] = useReducer((prev) => !prev, false);
return (
<div>
<button onClick={dispatchOpen}>Open</button>
<SidePane open={open} onClose={dispatchOpen} width={50} containerId="container">
<Component />
</SidePane>
</div>
);
}
This project did not appear by magic. It was started at Aitenders as a more modern and nicer way to display data and additionnal user actions.