@data-slot/resizable

中文文档
1.1.3发布于 2026/11/05MITTypeScriptESM + CJS构建来源已验证近 30 天下载 6925

Headless resizable panel groups for vanilla JavaScript. Accessible, unstyled, tiny.

原始使用文档
文档目录14 节
  1. Installation
  2. Quick Start
  3. API
  4. Initialization
  5. Slots
  6. Data Attributes
  7. Options
  8. Controller
  9. Events
  10. Styling
  11. Keyboard Navigation
  12. Accessibility
  13. Behavior
  14. License

@data-slot/resizable

Headless resizable panel groups for vanilla JavaScript. Accessible, unstyled, tiny.

Inspired by react-resizable-panels so that shadcn/ui-style component libraries can use it as a drop-in primitive.

Installation

npm install @data-slot/resizable

Quick Start

<div data-slot="resizable" data-direction="horizontal">
  <div data-slot="resizable-panel" data-default-size="50" data-min-size="20">Left</div>
  <div data-slot="resizable-handle" aria-label="Resize panels"></div>
  <div data-slot="resizable-panel" data-default-size="50">Right</div>
</div>

<script type="module">
  import { create } from "@data-slot/resizable";

  const controllers = create();
</script>

API

Initialization

create(scope?)

Auto-discover and bind all resizable groups in a scope (defaults to document).

import { create } from "@data-slot/resizable";

const controllers = create(); // Returns ResizableController[]

createResizable(root, options?)

Create a controller for a specific element.

import { createResizable } from "@data-slot/resizable";

const resizable = createResizable(element, {
  direction: "horizontal",
  keyboardResizeBy: 10,
  onLayoutChange: (layout) => console.log(layout),
});

Slots

SlotDescription
resizableRoot flex container for the group
resizable-panelA panel with a percentage size
resizable-handleFocusable separator between adjacent panels
<div data-slot="resizable">
  <div data-slot="resizable-panel">A</div>
  <div data-slot="resizable-handle" aria-label="Resize panels"></div>
  <div data-slot="resizable-panel">B</div>
</div>

A group needs at least one resizable-panel, and exactly one resizable-handle between each adjacent pair of panes.

Data Attributes

Options can also be set via data attributes. JS options take precedence.

On the root:

AttributeTypeDefaultDescription
data-directionstringhorizontalLayout axis
data-keyboard-resize-bynumber10Percent moved per arrow keypress

On each resizable-panel:

AttributeTypeDefaultDescription
data-default-sizenumbereven splitInitial size (%)
data-min-sizenumber0Minimum size (%)
data-max-sizenumber100Maximum size (%)
data-collapsiblebooleanfalsePane can collapse
data-collapsed-sizenumber0Size (%) when collapsed

Panel sizes and constraints must be between 0 and 100. minSize must not exceed maxSize, and a collapsible panel's collapsedSize must not exceed minSize. The constraints must allow a layout totaling 100%; initialization throws if they cannot. A collapsible panel can occupy its collapsed size or any size between its minimum and maximum.

Options

OptionTypeDefaultDescription
direction"horizontal" | "vertical""horizontal"Layout axis
keyboardResizeBynumber10Percent moved per arrow keypress
onLayoutChange(layout: number[]) => voidundefinedCalled when the layout changes

Controller

Pane indices start at zero. setLayout() requires one finite, non-negative number per panel and a positive total; it normalizes the values to 100% and applies the panel constraints. Invalid layouts throw without changing the current layout. resizePane() requires a finite size. Indexed mutation methods and getSize() throw for an invalid pane index.

Method/PropertyDescription
layoutCurrent layout as number[] of percentages (readonly)
setLayout(sizes)Set the full layout (validated/clamped)
resizePane(index, size)Resize a pane to size%
collapse(index)Collapse a collapsible pane
expand(index)Expand a collapsed pane
isCollapsed(index)Whether a pane is collapsed
isExpanded(index)Whether a pane is expanded
getSize(index)Current size (%) of a pane
destroy()Cleanup all listeners and global styles

Events

Outbound Events

element.addEventListener("resizable:change", (e) => {
  console.log("Layout:", e.detail.layout);
});

element.addEventListener("resizable:dragging", (e) => {
  console.log("Dragging:", e.detail.dragging);
});

Inbound Events

EventDetailDescription
resizable:set{ layout: number[] }Set the layout programmatically
element.dispatchEvent(new CustomEvent("resizable:set", { detail: { layout: [30, 70] } }));

Styling

The component sets flex styles on the root and panels directly. Give the group a height and the handles a visible width or height. Use data-* attributes for visual styling:

/* Style the handle */
[data-slot="resizable-handle"] {
  width: 4px;
  background: #ccc;
}

[data-slot="resizable"][data-direction="vertical"] [data-slot="resizable-handle"] {
  width: 100%;
  height: 4px;
}

/* Active drag / keyboard focus */
[data-slot="resizable-handle"][data-active] {
  background: #2563eb;
}

/* Collapsed pane */
[data-slot="resizable-panel"][data-collapsed] {
  opacity: 0;
}

Add a CSS transition on flex-grow for animated collapse/expand:

[data-slot="resizable-panel"] {
  transition: flex-grow 0.2s ease;
}

Keyboard Navigation

Focus a handle with Tab before using these keys:

KeyAction
ArrowLeft / ArrowRightResize a horizontal group by keyboardResizeBy (10% by default)
ArrowUp / ArrowDownResize a vertical group by the same step
Shift + arrow keyMove to the limit in that direction
Home / EndMinimize / maximize the preceding panel within constraints
EnterToggle collapse of the preceding panel when it is collapsible
F6 / Shift + F6Focus the next / previous handle, wrapping within the group

Accessibility

The component automatically handles:

  • role="separator" on each handle
  • aria-orientation (perpendicular to the layout direction)
  • aria-controls linking each handle to its preceding pane
  • aria-valuemin / aria-valuemax / aria-valuenow reflecting live constraints
  • Unique ID generation for the root, panes, and handles

Give each handle an accessible name with aria-label or aria-labelledby.

Behavior

Switching input methods

Keyboard resizing and valid imperative layout updates end an active drag before applying the change. Subsequent pointer movement has no effect until a new drag starts. Only one group can drag at a time in each document.

All input methods remember a panel's size immediately before it collapses. expand() and Enter restore that size, subject to the other panels' constraints. An initially collapsed panel expands to its minimum size when no prior size exists.

Persisting Layout

There is no built-in persistence, but reading and restoring the split is a one-liner:

import { createResizable } from "@data-slot/resizable";

const el = document.querySelector('[data-slot="resizable"]');
const saved = localStorage.getItem("layout:sidebar");

const resizable = createResizable(el, {
  onLayoutChange(layout) {
    localStorage.setItem("layout:sidebar", JSON.stringify(layout));
  },
});

if (saved) resizable.setLayout(JSON.parse(saved));

License

MIT

版本与发布记录

@data-slot/resizable 共发布 7 个版本,首次发布于 2026/09/24;过去 12 个月发布了 7 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.1.3latest2026/11/056 天前
1.1.22026/10/1428 天前
1.1.12026/10/051 个月前
1.1.02026/10/021 个月前
1.0.22026/09/271 个月前
1.0.12026/09/261 个月前

关于 @data-slot/resizable 的常见问题

@data-slot/resizable 是什么?

Headless resizable panel groups for vanilla JavaScript. Accessible, unstyled, tiny.

如何安装 @data-slot/resizable?

在项目目录运行 npm install @data-slot/resizable,或使用 pnpm add / yarn add / bun add。

@data-slot/resizable 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@data-slot/resizable 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@data-slot/resizable 可以商用吗?

@data-slot/resizable 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@data-slot/resizable 的下载量有多少?

根据 npm 官方下载统计,@data-slot/resizable 在 2026/10/12 – 2026/11/10 这 30 天内被下载 6,925 次,日均约 231 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@data-slot/resizable 还在维护吗?

最新版本 1.1.3 发布于 2026/11/05(6 天前);至今共发布 7 个版本,其中过去 12 个月发布了 7 个;首次发布于 2026/09/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。