Tailwind CSS Resizable Panels
Use the resizable component to split a layout into panels the user drags to size, like a sidebar next to content or an editor beside its preview. For a navigation column that collapses to icons, use the sidebar.
<div class="resizable max-w-md rounded-lg border">
<div class="resizable-panel" data-sp-size="50">
<div class="flex h-60 items-center justify-center p-6">
<span class="font-semibold">One</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="50">
<div class="resizable resizable-vertical">
<div class="resizable-panel" data-sp-size="25">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Two</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="75">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Three</span>
</div>
</div>
</div>
</div>
</div>Usage
Add the resizable class to a container with a height, and put resizable-panel elements inside it with a resizable-handle between each pair. Add resizable-vertical to the container to stack the panels instead, and put a resizable-grip inside a handle to show a visible grip. Nest a group inside a panel to split both ways.
<div class="resizable h-48">
<div class="resizable-panel">One</div>
<div class="resizable-handle"></div>
<div class="resizable-panel">Two</div>
</div>Examples
Vertical resizable panels
Add resizable-vertical to stack the panels and drag the split up and down.
<div class="resizable resizable-vertical min-h-60 max-w-md rounded-lg border">
<div class="resizable-panel" data-sp-size="25">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Header</span>
</div>
</div>
<div class="resizable-handle"></div>
<div class="resizable-panel" data-sp-size="75">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Content</span>
</div>
</div>
</div>Handle with a grip
Put a resizable-grip inside the handle to show where to drag.
<div class="resizable min-h-60 max-w-md rounded-lg border">
<div class="resizable-panel" data-sp-size="25">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Sidebar</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="75">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Content</span>
</div>
</div>
</div>Panel sizes
Set a panel's starting size with data-sp-size and its limits with data-sp-min and data-sp-max, as a percentage or in px, rem, em, vh, or vw.
<div class="resizable min-h-60 max-w-md rounded-lg border">
<div class="resizable-panel" data-sp-size="30" data-sp-min="120px" data-sp-max="50">
<div class="flex h-full items-center justify-center p-6 text-center text-sm">
<span class="font-semibold">Min 120px, max 50%</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-min="25">
<div class="flex h-full items-center justify-center p-6 text-sm">
<span class="font-semibold">Min 25%</span>
</div>
</div>
</div>Collapsible panel
Add data-sp-collapsible to let a panel snap shut when dragged past half its minimum, and data-sp-collapsed-size to leave a sliver showing.
<div class="resizable min-h-60 max-w-md rounded-lg border">
<div class="resizable-panel" data-sp-size="30" data-sp-min="20" data-sp-collapsible data-sp-collapsed-size="8">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Sidebar</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Content</span>
</div>
</div>
</div>Remember the layout
Add data-sp-persist with a key to store the layout in the browser and restore it on the next visit.
<div class="resizable min-h-60 max-w-md rounded-lg border" data-sp-persist="docs-demo">
<div class="resizable-panel" data-sp-size="40">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Files</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="60">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Editor</span>
</div>
</div>
</div>RTL
Add dir="rtl" to any parent element, typically <html>, to render right-to-left. See the RTL guide for details.
<div id="resizable-rtl-demo" class="resizable max-w-md rounded-lg border" dir="rtl">
<div class="resizable-panel" data-sp-size="50">
<div class="flex h-60 items-center justify-center p-6">
<span class="font-semibold" data-i18n="one">واحد</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="50">
<div class="resizable resizable-vertical">
<div class="resizable-panel" data-sp-size="25">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold" data-i18n="two">اثنان</span>
</div>
</div>
<div class="resizable-handle"><div class="resizable-grip"></div></div>
<div class="resizable-panel" data-sp-size="75">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold" data-i18n="three">ثلاثة</span>
</div>
</div>
</div>
</div>
</div>
<script>
const translations = {
ar: { dir: "rtl", one: "واحد", two: "اثنان", three: "ثلاثة" },
he: { dir: "rtl", one: "אחד", two: "שניים", three: "שלושה" },
en: { dir: "ltr", one: "One", two: "Two", three: "Three" },
};
window.addEventListener("message", (e) => {
const t = translations[e.data?.lang];
if (e.data?.type !== "sp-language" || !t) return;
document.getElementById("resizable-rtl-demo").dir = t.dir;
document.querySelectorAll("[data-i18n]").forEach((el) => {
el.textContent = t[el.dataset.i18n];
});
});
</script>Options
Set group options on the resizable element and size options on each resizable-panel. Sizes are a percentage of the group, or a length in px, rem, em, vh, or vw.
<div class="resizable h-64" data-sp-persist="workspace">
<div class="resizable-panel" data-sp-size="240px" data-sp-min="160px" data-sp-collapsible data-sp-preserve-pixels>
Sidebar
</div>
<div class="resizable-handle"></div>
<div class="resizable-panel">Content</div>
</div>A collapsed panel carries the collapsed class. A disabled panel keeps its size, so the handles next to it stop dragging. A panel with data-sp-preserve-pixels keeps its pixel width when the group changes size, while the other panels share the difference. A double click on a handle puts a neighbor back to its data-sp-size.
JavaScript
Events
Events fire on the resizable element. sp-resize fires on every change, sp-resized once a drag ends or a key press lands, both with { layout } as percentages. The collapse events carry { panel, index }.
el.addEventListener("sp-resized", (e) => {
console.log(e.detail.layout);
});Methods
Get the instance with sp.resizable(el), then call its methods. A panel is its index or its element.
const group = sp.resizable(document.querySelector("#workspace"));
group.resize(0, "320px");