← Back toDrag and drop in React

Create a drawer

Written byPhuoc Nguyen
Created
01 Nov, 2023
Last updated
10 Nov, 2023
Tags
React drawer
If you're not familiar with the term Drawer, don't worry. It's a common sidebar pattern that's usually found on the left or right side of a screen. Users can adjust the sidebar's size by dragging its left or right side.
The Drawer is a popular user interface pattern that provides quick access to application navigation and important information. It's a sidebar that can be easily hidden or revealed by the user. The Drawer component is a powerful tool for designing intuitive and user-friendly interfaces that help users focus on their tasks without distractions. It's also resizable, offering great flexibility to users in customizing their workspace.
The Drawer component is versatile and can be used in various applications, including productivity apps, email clients, and social media platforms. For example, Gmail's web application uses the Drawer component to allow users to quickly navigate between their inbox, sent messages, drafts, and other important folders. File management applications such as Windows Explorer or macOS Finder also use the Drawer to display file properties or navigation menus. The ability to resize the Drawer makes it perfect for viewing large amounts of content or data without taking up too much space on the screen. Whether you're designing a mobile app or a desktop application, the Drawer component can help you create an intuitive and user-friendly interface that enhances your users' experience.
In this post, we'll learn how to create a Drawer component using React.

Activating the drawer

While not always necessary, the drawer is typically located within a full-screen modal that opens when users click a specific button.
Here is a code snippet that utilizes a simple state to determine whether the modal is open or closed:
tsx
const [isOpened, setOpened] = React.useState(false);

// Open the modal
const handleOpenSidebar = () => setOpened(true);

// Close the modal
const handleCloseSidebar = () => setOpened(false);

// Render
<button onClick={handleOpenSidebar}>Open the modal</button>
{isOpened && (
<div className="modal">
<button onClick={handleCloseSidebar}>Close the modal</button>
</div>
)}
When a user clicks the button to open the modal, it triggers the `handleOpenSidebar` function. This sets the `isOpened` state to `true`, which causes the modal to appear on the screen.
Similarly, when the user clicks the Close button within the modal, it triggers the `handleCloseSidebar` function. This sets the `isOpened` state to `false`, and the modal disappears from the screen.
This simple state management technique allows us to easily control the visibility of our Drawer component. It's a straightforward way to manage the state of our application and keep our code clean and organized.
Good practice
To simplify a conditional rendering, try using the logical AND operator (`&&`) instead of an if-else statement. Here's an example to illustrate:
tsx
if (isOpened) {
<div className='modal'>...</div>
} else {
<></> {/* Don't render anything */}
}
Here is the better version:
tsx
{isOpened && (<div className="modal">...</div>)}
The `modal` class is important for positioning and sizing the modal on the screen. When the modal is open, we want it to cover the entire screen. To make this happen, we set the `position` property to `fixed`. This ensures that the modal stays put even when the user scrolls down the page.
We also set the `top`, `left`, `height`, and `width` properties to 0. This ensures that the modal takes up the entire screen, both horizontally and vertically.
Here's an example of how the `modal` CSS class could look:
css
.modal {
left: 0;
position: fixed;
top: 0;

height: 100%;
width: 100%;
}
To ensure that a modal is displayed on top of other elements, there are a few important things to keep in mind. First, we need to set the modal's `z-index` property to a high enough value. Typically, a value of 999 or 9999 does the trick.
css
.modal {
z-index: 999;
}
Secondly, it's essential to append the modal to the `body` element, making it the last element of the entire page. Luckily, React has an API that makes this easy as pie:
tsx
{
isOpened && ReactDOM.createPortal(
<div className="modal">...</div>,
document.body
);
}
The `createPortal` function is a handy tool that lets us render a component to a specific part of the HTML document. This is especially useful when we need to display a component outside of its parent hierarchy, like when we want to append it to the `body` element.
In our example, we use `createPortal` to render our modal component and attach it directly to the `document.body`. This ensures that the modal always appears on top of all other elements on the page.
By using this technique, we can keep our React components logically separated while still being able to interact with elements outside of their hierarchy. It's a great way to add flexibility and functionality to our components!

Setting up the drawer layout

The Drawer component is made up of three parts:
  • The first panel: This is the left or right panel, depending on how the drawer is set up. It displays the content that will appear in this section of the screen.
  • The resizer: This is the draggable element that separates the two panels. You can click and drag it to adjust the size of the main panel as needed.
  • The second panel: This is the other panel. This area doesn't display anything, and clicking on it should close the drawer.
Here's an example of a basic markup for a drawer, assuming that the main panel is located on the left:
tsx
<div className="drawer">
<div className="drawer__first">Content</div>
<div className="drawer__resizer" />
<div className="drawer__second" />
</div>
To organize the drawer layout, we can use CSS Flexbox. Flexbox makes it easy to align and position elements within a container. In this case, we want to divide the container into three parts: the first panel, the resizer, and the second panel.
To get started, we create a container element for our Drawer component and set its `display` property to `flex`. This turns the container into a flex container. We also set its `height` and `width` properties to `100%` so that it takes up the full height and width of its parent element.
css
.drawer {
display: flex;
height: 100%;
width: 100%;
}
Now let's create the first panel element. To do this, we'll assign it a class of `.drawer__first` and set its background color to white using the `background` property. In addition, we'll set its `width` property to `50%` so that it takes up half of the available space in our Drawer container.
css
.drawer__first {
background: #fff;
width: 50%;
}
Next up, we need to create the resizer element. We'll do this by giving it a class of `.drawer__resizer`, setting its background color with the `background` property, and defining its dimensions using the `height` and `width` properties. For this example, we're setting the width to `2px`.
css
.drawer__resizer {
background: rgb(203 213 225);
height: 100%;
width: 2px;
}
Finally, we'll create the second panel element. Simply give it a class of `.drawer__second` and set its dimensions using Flexbox's `flex` property. This will enable it to take up all the remaining space in our Drawer container, after accounting for the first panel and resizer.
css
.drawer__second {
flex: 1;
}
To make sure that users can still see the main page content while the drawer is open, we made the background color of the second panel transparent.
css
.drawer__second {
background: transparent;
}
Try out the following demo! Simply click the main button to open the Drawer.

Making the drawer resizable

A drawer is essentially a split view that you can resize. The left side displays the content of the drawer, while the right side is an empty area that allows users to close the drawer.
We won't go into the details of implementing the resizable feature of the drawer since we already demonstrated it in our previous post when creating a splitter component.
Let's talk about what happens if users accidentally click on the right panel while dragging the resizer to close the Drawer. Luckily, we've already thought about this issue. As users move the mouse, we update the cursor and disable user selection and pointer events to prevent any accidental clicks.
To solve this problem, we can use CSS styles like `userSelect` and `pointerEvents`. By setting these styles to `none`, we disable user selection and pointer events on the drawer's elements. This prevents users from accidentally selecting text or other content within the drawer while dragging the resizer. Disabling pointer events also ensures that any click or hover events are ignored on those elements when they are dragged, making it easier for users to interact with the drawer without any interruptions.
To refresh your memory, here's the code snippet we used earlier.
tsx
const updateCursor = () => {
const container = containerRef.current;
const firstHalfEle = firstHalfRef.current;
const resizerEle = resizerRef.current;
const secondHalfEle = secondHalfRef.current;

resizerEle.style.cursor = 'ew-resize';
document.body.style.cursor = 'ew-resize';

firstHalfEle.style.userSelect = 'none';
firstHalfEle.style.pointerEvents = 'none';
secondHalfEle.style.userSelect = 'none';
secondHalfEle.style.pointerEvents = 'none';
};
Ready to see it in action? Go ahead and give it a try by dragging the resizer in the middle!

Conclusion

To sum it up, the React Drawer component is an excellent tool for providing users with easy access to app navigation and important information. Its resizable feature offers great flexibility in customizing the workspace.
We can easily organize the layout and allow users to resize the drawer as needed by using CSS Flexbox and the technique we used to build a resizable Splitter component. Whether you're designing a mobile app or a desktop application, the Drawer component can help you create an intuitive and user-friendly interface that enhances the user experience. And with React's powerful toolset, implementing this popular UI pattern has never been easier.

See also

If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn