はじめに
VSCodeのエディタ画面を思い浮かべてください。サイドバーとエディタの境界をドラッグすると、パネルの幅が滑らかに変わりますよね。ああいう「ユーザーが自由にサイズを調整できる分割レイアウト」を、自分のReactアプリにも入れたいと思ったことはないでしょうか。
いざ自作しようとすると、これが意外と大変です。マウスイベントの追跡、最小・最大サイズの制約、タッチ操作への対応、キーボードでの操作性まで考え始めると、あっという間に数百行のコードになってしまいます。
そこで登場するのが React Resizable Panels です。React DevToolsの作者としても知られるBrian Vaughn氏が開発しているライブラリで、リサイズ可能なパネルレイアウトをわずか数行のJSXで実現できます。この記事では、2025年に大きく刷新されたv4系のAPIをベースに、基本から実践的な使い方までを解説します。
React Resizable Panelsとは
React Resizable Panelsは、ドラッグでサイズを変更できるパネルグループを構築するためのReactコンポーネントライブラリです。GitHubで5,000を超えるスターを集めており、shadcn/uiのResizableコンポーネントの内部実装としても採用されているため、知らないうちにお世話になっている方も多いかもしれません。
主な特徴
- 宣言的でシンプルなAPI -
Group・Panel・Separatorの3コンポーネントを組み合わせるだけで動きます - 柔軟なサイズ指定 - パーセンテージだけでなく、
px・em・rem・vh・vwといったCSS単位でサイズや制約を指定できます - アクセシビリティ対応 - セパレーターにはWAI-ARIAの
separatorロールが自動で付与され、キーボードでのリサイズ操作にも対応しています - 折りたたみ・命令的API - パネルの折りたたみ(collapse)や、コードからのサイズ変更もサポートしています
- TypeScript対応 - 型定義が同梱されており、専用のフックで命令的APIも型安全に扱えます
なお、v4ではコンポーネント名が旧来のPanelGroup・PanelResizeHandleからGroup・Separatorへと変更されました。検索で見つかる古い記事とはAPIが異なる場合があるので注意してください。
インストール
パッケージマネージャーでインストールするだけで、追加のCSSファイルなどは不要です。
# npm
npm install react-resizable-panels
# yarn
yarn add react-resizable-panels
# pnpm
pnpm add react-resizable-panels
基本的な使い方
まずは左右2分割のレイアウトを作ってみましょう。Groupの中にPanelを並べ、間にSeparatorを挟むだけです。
import { Group, Panel, Separator } from "react-resizable-panels";
export function SplitView() {
return (
<Group orientation="horizontal" style={{ height: "100vh" }}>
<Panel defaultSize="30%" minSize="20%">
<div>左パネル(サイドバー)</div>
</Panel>
<Separator style={{ width: 4, background: "#ccc" }} />
<Panel minSize="30%">
<div>右パネル(メインコンテンツ)</div>
</Panel>
</Group>
);
}
これだけで、境界をドラッグしてサイズを変更できるレイアウトが完成します。ポイントを整理します。
orientationで分割方向を指定します("horizontal"が横並び、"vertical"が縦並び。省略時は横並びです)defaultSize・minSize・maxSizeでサイズと制約を指定します。数値はピクセル、単位なしの文字列はパーセンテージとして解釈されます(例:defaultSize={200}は200px、defaultSize="30"は30%)Separatorは必須ではありませんが、キーボード操作でのリサイズが可能になるため設置が推奨されています。ダブルクリックでデフォルトサイズに戻る機能も付いてきます
ホバー時のスタイリング
セパレーターのドラッグ中やホバー時の見た目は、data-separator属性を使ってCSSでカスタマイズできます。
[data-separator] {
width: 4px;
background: #e2e8f0;
transition: background 0.15s;
}
[data-separator]:hover {
background: #3b82f6;
}
実践的なユースケース
折りたたみ可能なサイドバー
エディタ系のUIでよくある「一定より狭くしようとするとパクッと閉じるサイドバー」は、collapsibleプロパティで実現できます。さらにusePanelRefフックを使えば、ボタンから開閉を制御することもできます。
import { Group, Panel, Separator, usePanelRef } from "react-resizable-panels";
export function EditorLayout() {
const sidebarRef = usePanelRef();
const toggleSidebar = () => {
const sidebar = sidebarRef.current;
if (!sidebar) return;
sidebar.isCollapsed() ? sidebar.expand() : sidebar.collapse();
};
return (
<div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<header>
<button onClick={toggleSidebar}>サイドバー切り替え</button>
</header>
<Group orientation="horizontal" style={{ flex: 1 }}>
<Panel
panelRef={sidebarRef}
collapsible
collapsedSize={0}
defaultSize="25%"
minSize="15%"
maxSize="40%"
>
<nav>ファイルツリー</nav>
</Panel>
<Separator />
<Panel minSize="40%">
<main>エディタ本体</main>
</Panel>
</Group>
</div>
);
}
panelRef経由で公開される命令的APIには、collapse() / expand() / resize(size) / getSize() / isCollapsed()があります。「ボタンひとつでパネルを開閉したい」「特定の操作でサイズをリセットしたい」といった要件にそのまま応えられます。
3分割の縦横ネストレイアウト
Groupは入れ子にできるので、IDEのような複雑なレイアウトも宣言的に書けます。下段にターミナル風のパネルを持つ構成の例です。
import { Group, Panel, Separator } from "react-resizable-panels";
export function IdeLayout() {
return (
<Group orientation="horizontal" style={{ height: "100vh" }}>
<Panel defaultSize="20%" minSize={160}>
<nav>エクスプローラー</nav>
</Panel>
<Separator />
<Panel>
{/* 右側をさらに上下に分割 */}
<Group orientation="vertical">
<Panel minSize="30%">
<main>エディタ</main>
</Panel>
<Separator />
<Panel defaultSize="25%" minSize={100} collapsible>
<section>ターミナル</section>
</Panel>
</Group>
</Panel>
</Group>
);
}
minSize={160}のように数値で指定すれば「160pxより狭くならない」という制約になります。パーセンテージとピクセルを混在できるのは、他の類似ライブラリにはあまりない強みです。
レイアウトをlocalStorageに保存する
ユーザーが調整したレイアウトを次回訪問時にも復元したい、というのは定番の要件です。onLayoutChangedとdefaultLayoutを組み合わせて実現できます。
import { Group, Panel, Separator, type Layout } from "react-resizable-panels";
const STORAGE_KEY = "app-layout";
function loadLayout(): Layout | undefined {
try {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? (JSON.parse(saved) as Layout) : undefined;
} catch {
return undefined;
}
}
export function PersistentLayout() {
return (
<Group
orientation="horizontal"
defaultLayout={loadLayout()}
onLayoutChanged={(layout) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
}}
style={{ height: "100vh" }}
>
<Panel id="sidebar" defaultSize="30%">
<div>サイドバー</div>
</Panel>
<Separator />
<Panel id="main">
<div>メイン</div>
</Panel>
</Group>
);
}
ここでonLayoutChange(現在進行形)ではなくonLayoutChanged(完了形)を使っているのがポイントです。前者はドラッグ中にポインターが動くたびに呼ばれるのに対し、後者はドラッグが終わったタイミングで呼ばれるため、ストレージへの保存にはこちらが推奨されています。各Panelにidを振っておくと、保存されたレイアウトとパネルの対応づけが安定します。
まとめ
React Resizable Panelsを使えば、自作すると骨の折れるリサイズ可能なレイアウトを、宣言的なコンポーネントの組み合わせだけで構築できます。最後に要点を振り返ります。
Group・Panel・Separatorの3つを組み合わせるだけで分割レイアウトが完成します- サイズはパーセンテージとピクセルを混在でき、
minSize・maxSize・collapsibleで挙動を細かく制御できます usePanelRefによる命令的APIで、ボタンからの開閉やサイズ変更も簡単ですonLayoutChangedを使えばレイアウトの永続化も数行で実装できます
ダッシュボード、コードエディタ、ログビューアなど「ユーザーごとに見たい情報のバランスが違う」画面には特に効果的です。まずは2分割のシンプルな構成から、ぜひ試してみてください。