React ガイド
@reelkit/react でスライダーを作る方法を説明します。
Reel コンポーネント
Reel コンポーネントはメインのコンテナーです。スライダーの状態を管理し、タッチジェスチャー、キーボード操作、アニメーションを処理します。
DOM に置かれるスライドは常に 3 枚、現在のスライドとその前後だけなので、1 万件のリストも 10 件のリストと同じ負荷で済みます。無限フィードやプレーヤーを開くグリッドなど、機能全体での使い方はレシピを参照してください。
import { Reel, ReelIndicator } from '@reelkit/react';
<Reel
count={items.length}
size={[width, height]}
direction="vertical"
enableWheel
afterChange={(index) => console.log('Current:', index)}
itemBuilder={(index, indexInRange, size) => (
<div style={{ width: size[0], height: size[1] }}>
Slide {index}
</div>
)}
>
{/* Optional children like ReelIndicator */}
</Reel>サイズの自動計測
size プロップは任意です。省略すると、Reel は ResizeObserver でコンテナーを自動計測し、CSS で決まるレイアウトに合わせます。コンテナーのサイズは親要素で決める必要があります(flex、grid、または CSS での明示的な寸法など)。
// Explicit size (fixed)
<Reel count={items.length} size={[400, 600]} itemBuilder={...} />
// Auto-size (responsive — sized by CSS)
<Reel count={items.length} style={{ width: '100%', height: '100dvh' }} itemBuilder={...} />itemBuilder パターン
itemBuilder プロップは、インデックスを受け取って各スライドの内容を返す関数です。このパターンによって仮想化が可能になり、表示中のアイテムだけが描画されます。
itemBuilder={(index, indexInRange, size) => {
// index: actual item index (0 to count-1)
// indexInRange: position in visible window (0, 1, or 2)
// size: [width, height] of the container
return <Slide index={index} />;
}}ナビゲーション
組み込みの操作方法:
- タッチ/スワイプ: ドラッグで移動し、慣性とスナップが効きます
- キーボード: 矢印キーと Escape
- マウスホイール:
enableWheelプロップで有効にします - プログラムから:
apiRefでnext()、prev()、goTo()を呼びます
import { useRef } from 'react';
import { Reel, type ReelApi } from '@reelkit/react';
function App() {
const apiRef = useRef<ReelApi>(null);
return (
<>
<Reel
count={10}
size={[400, 600]}
apiRef={apiRef}
itemBuilder={(index) => <Slide index={index} />}
/>
<button onClick={() => apiRef.current?.prev()}>Prev</button>
<button onClick={() => apiRef.current?.next()}>Next</button>
<button onClick={() => apiRef.current?.goTo(5)}>Go to 5</button>
</>
);
}URL の状態
useOverlayUrlState はオーバーレイ用の URL 状態コントローラーを作り、そのまま返します。それを *UrlOverlay の controller プロップに渡します。開閉状態は URL が持つので、結び付けられたオーバーレイは自分で開き、開く操作は通常リンクになります。存在しないパラメーターへの最初の書き込みは履歴エントリーを 1 つ積み、以降の書き込みはそれを置き換えるので、ページを送っても戻るボタンが埋もれることはありません。コントローラーは手元に残しておき、value/position を読んだり、プログラムから操作したりするのに使います。set(position) で開き、set(null) で閉じます。set は、オーバーレイがスライドの切り替え時に内部で使っているのと同じ低レベルの書き込みです。
import { useOverlayUrlState, urlIndexKey } from '@reelkit/react';
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';
const photo = useOverlayUrlState({
param: 'photo',
...urlIndexKey(() => images.length),
});
// Opening is a link — the overlay reads the URL and opens itself.
<Link to="?photo=3"><img src={images[3].src} /></Link>
<LightboxUrlOverlay controller={photo} images={images} />
// Read the url-derived state, or close programmatically (a low-level write).
photo.position.value; // 3 for ?photo=3, null when nothing is open
photo.set(null); // close
// Routed app: pass a router-backed adapter, otherwise the router's
// own location goes stale and its next navigation drops the param.
const adapter = useReactRouterUrlAdapter();
const routed = useOverlayUrlState({
param: 'photo',
adapter,
...urlIndexKey(() => images.length),
});オプションのオブジェクトは param、codec、locator(3 つとも必須)と、任意の adapter を受け取ります。param と adapter は最初の描画で読まれ、その後は固定です(変えるには再マウントします)。一方 codec と locator は毎回読まれるので、リストが伸びたり並べ替えられたりしたあとの検索では、現在のリストが見えます。codec と locator は同じ Id を共有する対なので、一緒に扱います。単純な ?photo=3 のギャラリーなら ...urlIndexKey(() => images.length) を展開すれば、両方が一度に返されます。urlIndexKey はパラメーターをスライドのインデックスに対応させ、getter が返す現在の件数で範囲を制限するので、古いまたは範囲外の ?photo=99 は拒否され、指されていないスライドを開く代わりに URL から自動で取り除かれます。数値ではなく getter を渡してください。そうすればページングされたフィードが伸びても範囲が正しく保たれます。内部では createIndexLocator(locator の側)を包み、indexCodec と組み合わせています。ページングされたフィードや識別子で指すギャラリーは、代わりに独自の codec と locator の対を用意します。すべてのオプションの表は React API リファレンス にあります。
ReelIndicator
スライダー内の現在位置を示す、Instagram 風の進捗インジケーターを表示する任意のコンポーネントです。Reel の中に置くと、コンテキストを通じて親の count と active の値に自動で接続するので、状態を手でつなぐ必要はありません。
import { Reel, ReelIndicator } from '@reelkit/react';
{/* Auto-connect: count and active are inherited from parent Reel */}
<Reel count={10} size={[400, 600]} itemBuilder={...}>
<ReelIndicator />
</Reel>
{/* Manual usage: pass count and active explicitly (e.g. outside a Reel) */}
<ReelIndicator count={10} active={currentIndex} />ポイント
- size プロップ
任意の [幅, 高さ] のタプルです。省略すると CSS でサイズが決まります
- itemBuilder
インデックスを受け取り、スライドの内容を返します
- apiRef
ナビゲーションのためにコントローラーのメソッドへアクセスします
- afterChange
UI を更新するために現在のインデックスを追跡します
パフォーマンスのヒント
- データの配列をメモ化する
アイテムの配列を
useMemoで包んでください。描画のたびに配列の参照が新しくなると、countの更新と表示範囲の再計算が起きます。 - itemBuilder を軽く保つ
表示範囲が変わるたびに(通常は 3 枚分)実行されます。中で重い計算や副作用を行わないでください。
- 端に近づいたらデータを読み込む
afterChangeでユーザーが終わりに近づいたことを検知し、スライドが尽きる前に次のバッチを取得します(レシピ 無限フィード を参照)。 - スクロールするページではホイールを無効にする
スクロールするレイアウトの中にスライダーを埋め込むときは、ページのスクロールを奪わないよう
enableWheel={false}を指定してください。
次のステップ
- API リファレンス - 利用できるすべての props
- Reel Player - TikTok/Reels 風の動画プレイヤー
- Lightbox - 画像と動画のギャラリー
- Stories Player - Instagram 風のストーリービューアー