画面幅をドラッグで動かす「react-resizable-panels」
投稿日: 2025年03月25日
JS Gymの開発で
「エディタの幅を変えられるようにしてほしい」
というフィードバックを複数いただいていまして、実装しました。
ドラッグ&ドロップ機能の一番有名なライブラリとしてReact DnDがありますが、
結構実装が面倒で「使うほどかな?」と思っていたところ
「react-resizable-panels」というちょうど良いライブラリを見つけたので使いました。
公式サイトもわかりやすく、サンプルもあります。
https://react-resizable-panels.vercel.app/
パネルをドラッグして画面のサイズ変更できる
横方向・縦方向、どちらにも対応
初期サイズ指定・最小サイズも指定可能
npm install react-resizable-panels
import React from "react";
import {
PanelGroup,
Panel,
PanelResizeHandle,
} from "react-resizable-panels";
export default function App() {
return (
<div className="h-screen">
<PanelGroup direction="horizontal">
<Panel defaultSize={50}>
<div className="h-full p-4 bg-blue-100">
<p>左側の画面</p>
</div>
</Panel>
<PanelResizeHandle>
<div className="w-2 cursor-col-resize bg-gray-300" />
</PanelResizeHandle>
<Panel defaultSize={50}>
<div className="h-full p-4 bg-green-100">
<p>右側の画面</p>
</div>
</Panel>
</PanelGroup>
</div>
);
}
ポイント
PanelGroup
の direction
に "horizontal"
を指定すると左右に分割される
PanelResizeHandle
が中央のドラッグバー
Panel
の defaultSize
は初期サイズ(%)
調整後に画面を再読み込みしても、幅がキープされるようにしたかったですが、
DBに保存するほどでも無いと思い、localStorageに保存するようにしました。
まず、ローカルストレージを管理するカスタムフックを作ります。
useLocalStorage.ts
import { useState, useEffect } from "react";
export function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T) => void] {
const readValue = () => {
if (typeof window === "undefined") return initialValue;
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
};
const [storedValue, setStoredValue] = useState<T>(readValue);
const setValue = (value: T) => {
setStoredValue(value);
if (typeof window !== "undefined") {
localStorage.setItem(key, JSON.stringify(value));
}
};
useEffect(() => {
const onStorage = (e: StorageEvent) => {
if (e.key === key && e.newValue) {
setStoredValue(JSON.parse(e.newValue));
}
};
window.addEventListener("storage", onStorage);
return () => window.removeEventListener("storage", onStorage);
}, [key]);
return [storedValue, setValue];
}
App.tsx
export default function App() {
const [sizes, setSizes] = useLocalStorage<number[]>("panel-sizes", [50, 50]);
return (
<div className="h-screen">
<PanelGroup
direction="horizontal"
onLayout={(newSizes) => setSizes(newSizes)}
>
<Panel defaultSize={sizes[0]}>
<div className="h-full p-4 bg-blue-100">
<p>左側の画面</p>
</div>
</Panel>
<PanelResizeHandle>
<div className="w-2 cursor-col-resize bg-gray-300" />
</PanelResizeHandle>
<Panel defaultSize={sizes[1]}>
<div className="h-full p-4 bg-green-100">
<p>右側の画面</p>
</div>
</Panel>
</PanelGroup>
</div>
);
}
サイドバーの幅調整とか、いろんなところにサクッと導入できそうな感じなので、
おすすめのライブラリです。