DEV Community

vikas bansal
vikas bansal

Posted on

Creating an iOS-like Slide-Up Drawer with React Portals

So...

Have you ever been browsing a website, perhaps on your mobile device, and marvelled at the smooth, elegant slide-up drawers that seamlessly appear at the bottom of the screen? Or maybe you've interacted with a modal that felt so intuitive and flexible that you wondered, "How do they do that?" Well, that's precisely the question I had in mind. I wanted to create a slide-up drawer that not only looks great but is also highly reusable and can accept any kind of children. This journey led me to discover the power of React Portals.

The Drawer

Image description

and...

The task was clear, I needed to create a slide-up drawer in React that would always be a sibling of the <body> element, ensuring it would overlay everything else on the page. It also needed to be flexible enough to accept any UI components as its content. Achieving this while maintaining a smooth user experience and clean codebase seemed like a daunting task.

hello portals...

The solution came in the form of React Portals. React Portals provide a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. This feature is perfect for creating modals, tooltips, and, in my case, a slide-up drawer.

Here’s how I did it.

GitHub now has everything and so does this code... here is the repository
and the Live Preview too

Note: Open the dev console and change the viewport to mobile view to see the drawer in action 😄

goals...

Separation of Concerns: By using React Portals, I could keep the drawer's DOM structure separate from the parent component's DOM, leading to better-organized code.

Reusability: The drawer component could accept any children, making it highly reusable across different parts of the application.

Smooth Animations: With CSS transitions, I could achieve smooth and visually appealing slide-up and slide-down animations.

code...

Step 1: Creating the SlideUpDrawer Component

React Portals allow you to render a component outside its parent component's DOM hierarchy. This means you can create a component that appears as a sibling to the <body> element instead of being nested inside another component. This is especially useful for components like modals, tooltips, and our slide-up drawer because it ensures that they overlay everything else on the page.

Here's how you can create a slide-up drawer using React Portals:

import React, { useState, ReactNode } from 'react';
import ReactDOM from 'react-dom';
import './SlideUpDrawer.css';

interface SlideUpDrawerProps {
    children: ReactNode;
}

const SlideUpDrawer: React.FC<SlideUpDrawerProps> = ({ children }) => {
    const [isOpen, setIsOpen] = useState(false);

    const toggleDrawer = () => {
        setIsOpen(!isOpen);
    };

    return ReactDOM.createPortal(
        <div className="drawer-container">
            <button className="open-drawer-button" onClick={toggleDrawer}>
                {isOpen ? 'Close' : 'Open'} Drawer
            </button>
            <div className={`drawer ${isOpen ? 'open' : ''}`}>
                <div className="drawer-content">
                    {children}
                    <button onClick={toggleDrawer}>Close</button>
                </div>
            </div>
        </div>,
        document.body
    );
};

export default SlideUpDrawer;
Enter fullscreen mode Exit fullscreen mode

Step 2: Styling
Next, I wrote some CSS, and the goal was to achieve a smooth slide-up and slide-down effect.

I am using CSS transitions, which allow you to change property values smoothly over a given duration. This makes animations like sliding a drawer up and down feel natural and responsive. The transition property in CSS is key to achieving this smoothness.

  • The .drawer class is initially positioned off-screen (bottom: -100%).
  • When the .drawer.open class is added, it slides up to bottom: 0.
  • The transition: bottom 0.3s ease ensures the slide-up and slide-down animations are smooth and take 0.3 seconds.

Read this awesome blog on great animations by Emil Kowalski

.drawer-container {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 50%;
    overflow: hidden;
    z-index: 1000;
}

.open-drawer-button {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1001;
}

.drawer {
    position: fixed;
    bottom: -100%;
    left: 0;
    width: 100%;
    height: 50%;
    background-color: white;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
    transition: bottom 0.3s ease;
    z-index: 1000;
}

.drawer.open {
    bottom: 0;
}

.drawer-content {
    padding: 20px;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Usage
To see the drawer in action, use the SlideUpDrawer in the main application component and pass some children to it.

import React from 'react';
import SlideUpDrawer from './SlideUpDrawer';

const App: React.FC = () => {
    return (
        <div>
            <h1>Slide Up Drawer Example</h1>
            <SlideUpDrawer>
                <p>This is the content of the drawer.</p>
                <p>You can add any UI components here!</p>
            </SlideUpDrawer>
        </div>
    );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

understanding...

By leveraging React Portals, I was able to create a versatile and reusable slide-up drawer component in React. This component is reusable, capable of accepting any children and smooth 🤩

React Portals provided the key solution to rendering the drawer as a sibling of the <body> element, maintaining a clean and manageable codebase. The combination of TypeScript and CSS transitions ensured a robust and smooth user experience.

Feel free to customize and extend the component to fit your specific needs. Contributions and improvements are always welcome!

Top comments (0)