How to build a simple off-canvas panel in React
On my last few React projects I've had a need for a simple off-canvas panel. One was for the mobile menu on my portfolio site and another was for a web app I was building in my day job with a London local authority. The functionality for these panels in both projects were pretty simple. For the mobile navigation, the panel would open and appear on-canvas when the hamburger menu was clicked and would close and return off-canvas when navigating to another page of my website. The web app I was building was opened when a row on a table of data was clicked and closed when the the close button on the panel was clicked. All fairly simple stuff...
However, with the web app I was tasked with building it using Microsoft's Fluent UI design system. I initially attempted to use the panel component in the design system, but felt all of the options provided were overkill and required a lot of boiler plate code for something so simple. FluentUI also uses Typescript and I wasn't using Typescript on this project as it was a simple proof of concept piece of work and again meant more boilerplate code.
In the end I built my own component and was able to reuse it across projects. So here's my code and how I did it.
I started by building the panel component first. This component would take a toggle and event prop. The toggle prop simply toggles the open/close class on the element. The animation is handled by CSS.
When using the panel for the mobile navigation, you'll want the panel to automatically close when a menu link in the panel is clicked, to do this add an onClick={togglePanel}
to the panel element.
const Panel = ({ children, toggle, event }) => {
return (
<section
className={`panel ${toggle}`}
onClick={event}
>
{children}
</section>
);
};
.panel {
padding: 15px 15px;
display: flex;
flex-flow: column;
position: fixed;
top: 0;
bottom: 0;
height: 100vh;
overflow: hidden;
z-index: 999;
background-color: #334050;
visibility: visibility;
-moz-transition: right 500ms ease !important;
-ms-transition: right 500ms ease !important;
-webkit-transition: right 500ms ease !important;
-o-transition: right 500ms ease !important;
transition: right 500ms ease !important;
box-shadow: 0px 0.6px 1.8px rgba(0, 0, 0, 0.1),
0px 3.2px 7.2px rgba(0, 0, 0, 0.13);
}
.closed {
right: -100vw;
z-index: 9999;
}
.open {
right: 0;
z-index: 9999;
}
To use the panel, we manage some simple state in app.js or whatever parent component we use it in. This will set the toggle state. It's set to closed initially so so we give it the false
boolean. We then use a simple event handler to run the toggle event which toggles the class on the panel element.
const App = () => {
const [panel, setPanel] = useState(false);
const togglePanel = () => setPanel(!panel);
let toggle = panel ? `open` : `close`;
const openPanel = () => {
togglePanel();
};
return (
<Panel toggle={toggle} event={togglePanel}>
{/* content */}
</Panel>
)
}
And it's as simple as that.