The usual Tailwind pattern is to keep the sidebar in the DOM and animate with transform instead of toggling hidden.
hidden removes the element from layout, so there is nothing to transition. For a mobile drawer, use a fixed overlay panel that moves in/out with -translate-x-full and translate-x-0, then make it permanently visible on desktop with responsive classes.
Basic approach
- On mobile: sidebar is
fixed, slides from the left. - On desktop: sidebar becomes
staticorstickyand stays visible. - Use a state variable to toggle the mobile drawer.
- Add a backdrop if you want the common “drawer” feel.
Example
import { useState } from "react";
export default function DashboardLayout({ children }) {
const [open, setOpen] = useState(false);
return (
<div className="min-h-screen bg-gray-100 md:flex">
{/* Mobile header */}
<div className="flex items-center justify-between bg-white p-4 shadow md:hidden">
<button
onClick={() => setOpen(true)}
className="rounded bg-gray-900 px-3 py-2 text-white"
>
Menu
</button>
</div>
{/* Backdrop */}
<div
className={`fixed inset-0 z-40 bg-black/50 transition-opacity md:hidden ${
open ? "opacity-100" : "pointer-events-none opacity-0"
}`}
onClick={() => setOpen(false)}
/>
{/* Sidebar */}
<aside
className={`
fixed inset-y-0 left-0 z-50 w-[250px] bg-white shadow-lg
transform transition-transform duration-300 ease-in-out
${open ? "translate-x-0" : "-translate-x-full"}
md:static md:translate-x-0 md:shadow-none
`}
>
<div className="p-6 font-semibold">Sidebar</div>
<nav className="space-y-2 p-6">
<a href="#" className="block rounded px-3 py-2 hover:bg-gray-100">Dashboard</a>
<a href="#" className="block rounded px-3 py-2 hover:bg-gray-100">Reports</a>
<a href="#" className="block rounded px-3 py-2 hover:bg-gray-100">Settings</a>
</nav>
</aside>
{/* Main content */}
<main className="flex-1 p-6 md:ml-[250px]">
<button
onClick={() => setOpen(false)}
className="mb-4 rounded bg-gray-200 px-3 py-2 md:hidden"
>
Close
</button>
{children}
</main>
</div>
);
}
Why this works
- Transforms animate smoothly in CSS, so the slide-in effect is clean.
- Responsive overrides like
md:static md:translate-x-0make the sidebar stay visible on desktop. - Desktop spacing is handled by
md:ml-[250px]on the content area, or you can use a flex layout with a fixed-width sidebar.
Notes
- If you want the sidebar to push content on desktop, use a flex layout and a fixed width on the sidebar.
- If you want it to overlay content on mobile, keep it
fixedthere. - You can replace the hard-coded
250pxwith a Tailwind arbitrary value likew-[250px]if that matches your design.
So the key idea is: don’t use hidden for the animated state; use translate-x with responsive positioning classes instead.