React / Types of React Portal / Sidebar Drawer Portal
Home /React /Types of React Portal /Sidebar Drawer Portal

Sidebar Drawer Portal

Sidebar Drawer Portal

A sidebar drawer slides in from the left or right side of the viewport. Like modals, drawers
need to be outside the component tree’s DOM container to avoid overflow or z-index issues.

Drawer.jsx


import { createPortal } from 'react-dom';
import { useEffect } from 'react';

function Drawer({ isOpen, onClose, side = 'right', children }) {
  // Lock background scroll
  useEffect(() => {
    document.body.style.overflow = isOpen ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [isOpen]);

  // Escape to close
  useEffect(() => {
    const handler = (e) => e.key === 'Escape' && onClose();
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [onClose]);

  if (!isOpen) return null;

  return createPortal(
    <>
      {/* Backdrop */}
      <div
        onClick={onClose}
        style={{
          position: 'fixed',
          inset: 0,
          background: 'rgba(0,0,0,0.4)',
          zIndex: 8888
        }}
      />
      {/* Drawer panel */}
      <div
        role="dialog"
        aria-modal="true"
        style={{
          position: 'fixed',
          top: 0,
          bottom: 0,
          [side]: 0,
          width: '320px',
          background: '#fff',
          boxShadow: side === 'right' ? '-4px 0 16px rgba(0,0,0,0.2)' : '4px 0 16px rgba(0,0,0,0.2)',
          zIndex: 9999,
          overflowY: 'auto',
          padding: '1.5rem'
        }}
      >
        <button onClick={onClose} aria-label="Close drawer">✕</button>
        {children}
      </div>
    </>,
    document.body
  );
}

export default Drawer;

Usage


import { useState } from 'react';
import Drawer from './Drawer';

function App() {
  const [open, setOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setOpen(true)}>Open Drawer</button>

      <Drawer isOpen={open} onClose={() => setOpen(false)} side="right">
        <h2>Sidebar Navigation</h2>
        <ul>
          <li>Home</li>
          <li>Profile</li>
          <li>Settings</li>
        </ul>
      </Drawer>
    </div>
  );
}