react-joyride について
React Joyride は、React アプリの画面を案内するツアーを作成するライブラリです。フックとコンポーネントの API を備え、ステップ設定、非同期フック、イベント処理、UI のカスタマイズに対応します。
主な機能
- 01フックとコンポーネントの API
- 02ツアーのステップと対象の設定
- 03非同期の前後フック
- 04イベント処理とプログラム制御
- 05スタイルとコンポーネントのカスタマイズ
活用シーン
- 01アプリ画面を通じたユーザー案内
- 02複数ステップでの操作説明
- 03ステップ表示前の UI 変更待ち
- 04ツアーの進行状況とイベント追跡
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gilbarbara/ react-joyride --skill react-joyrideインストール後、Claude Code で/react-joyrideを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-events-components.mdapi-props-options.mdapi-step-state-controls.mdpatterns.md詳細
React Joyride v3
React アプリでガイドツアーを作成できます。公開 API は 2 つあります。useJoyride() フック(推奨)と <Joyride> コンポーネントです。
オンラインドキュメント:https://v3.react-joyride.com
クイックスタート
フックを使う(推奨)
import { useJoyride, STATUS, Status } from 'react-joyride';
function App() {
const { Tour } = useJoyride({
continuous: true,
run: true,
steps: [
{ target: '.my-element', content: 'This is the first step', title: 'Welcome' },
{ target: '#sidebar', content: 'Navigate here', placement: 'right' },
],
onEvent: (data) => {
if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) {
// Tour ended
}
},
});
return <div>{Tour}{/* rest of app */}</div>;
}
コンポーネントを使う
import { Joyride, STATUS, Status } from 'react-joyride';
function App() {
return (
<Joyride
continuous
run={true}
steps={[
{ target: '.my-element', content: 'First step' },
{ target: '#sidebar', content: 'Second step' },
]}
onEvent={(data) => {
if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) {
// Tour ended
}
}}
/>
);
}
フックは { controls, failures, on, state, step, Tour } を返します。JSX で Tour をレンダリングしてください。
ドキュメント:https://v3.react-joyride.com/docs/getting-started
コアコンセプト
ツアーには 2 つの状態軸があります。
ツアーのステータス:idle -> ready -> waiting -> running <-> paused -> finished | skipped
idle:ステップが読み込まれていませんready:ステップが読み込まれ、run: trueになるのを待っていますwaiting:run=trueですが、ステップを非同期で読み込んでいます(ステップが到着すると running に遷移します)running:ツアーが実行中ですpaused:ツアーが一時停止されています(COMPLETE 時の制御モード、またはstop()の呼び出し)finished/skipped:ツアーが終了しました
ステップのライフサイクル(各ステップ):init -> ready -> beacon_before -> beacon -> tooltip_before -> tooltip -> complete
*_beforeフェーズ:ここでスクロールと位置決めが行われますbeacon:脈動するインジケーターが表示されます(continuousで移動中の場合、skipBeaconの場合、またはplacement: 'center'の場合はスキップされます)tooltip:ツールチップが表示され、操作可能になります
ドキュメント:https://v3.react-joyride.com/docs/how-it-works
ステップの設定
各ステップには target と content が必要です。それ以外のフィールドはすべて省略可能です。
{
target: '.my-element', // CSS selector, HTMLElement, React ref, or () => HTMLElement
content: 'Step body text', // ReactNode
title: 'Optional title', // ReactNode
placement: 'bottom', // Default. Also: top, left, right, *-start, *-end, auto, center
id: 'unique-id', // Optional identifier
data: { custom: 'data' }, // Attached to event callbacks
}
ターゲットの種類
// CSS selector
{ target: '.sidebar-nav' }
// HTMLElement
{ target: document.getElementById('my-el') }
// React ref
const ref = useRef(null);
{ target: ref }
// Function (evaluated each lifecycle)
{ target: () => document.querySelector('.dynamic-element') }
よく使うステップオプション(ステップごとに上書き可能)
| オプション | デフォルト | 説明 |
|---|---|---|
placement | 'bottom' | ツールチップの位置です。モーダルスタイルにするには 'center' を使います(target: 'body' が必要です) |
skipBeacon | false | ビーコンをスキップし、ツールチップを直接表示します |
buttons | ['back','close','primary'] | ツールチップ内のボタンです。スキップボタンを追加するには 'skip' を指定します |
hideOverlay | false | 暗いオーバーレイを表示しません |
blockTargetInteraction | false | ハイライトされた要素へのクリックをブロックします |
before | - | (data) => Promise<void> — ステップ表示前に実行する非同期フックです |
after | - | (data) => void — ステップ完了後に実行する非同期処理を待たないフックです |
skipScroll | false | ターゲットまでスクロールしません |
scrollTarget | - | target の代わりにこの要素までスクロールします |
spotlightTarget | - | target の代わりにこの要素をハイライトします |
spotlightPadding | 10 | スポットライトの周囲の余白です。数値、または { top, right, bottom, left } を指定します |
targetWaitTimeout | 1000 | ターゲットが表示されるまで待つ時間(ミリ秒)です。0 = 待機しません |
beforeTimeout | 5000 | before フックを待つ時間(ミリ秒)です。0 = タイムアウトしません |
すべての Options フィールドは、options プロパティを使ってグローバルに設定するか、ステップごとに設定できます。ステップごとの値はグローバル設定を上書きします。
ドキュメント:https://v3.react-joyride.com/docs/step | https://v3.react-joyride.com/docs/props/options
非制御モードと制御モード
非制御(デフォルト — 強く推奨)
ツアーがステップの移動を内部で管理します。ほとんどのユースケースではこちらが適切です。
ライブラリが非同期の遷移を処理します。 ステップで UI の変更(ドロップダウンを開く、データを読み込む、アニメーションなど)を待つ必要がある場合は、before フックを使います。ツアーはプロミスが解決するまで待ってからステップを表示します。ターゲット要素がまだ DOM にない場合は、targetWaitTimeout(デフォルト:1000 ミリ秒)によってポーリングされます。こうしたケースで制御モードを使う必要はありません。
const { Tour } = useJoyride({
continuous: true,
run: isRunning,
steps: [
{ target: '.nav', content: 'Navigation' },
{
target: '.dropdown-item',
content: 'Inside the dropdown',
before: () => {
// Open dropdown and wait for animation — tour waits automatically
openDropdown();
return new Promise(resolve => setTimeout(resolve, 300));
},
after: () => closeDropdown(), // Clean up after step (fire-and-forget)
},
{ target: '.main-content', content: 'Main content' },
],
onEvent: (data) => {
if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(data.status)) {
setIsRunning(false);
}
},
});
制御(stepIndex を使用)— 必要な場合にのみ使用
親がステップインデックスを外部で管理する必要がある場合に限り、制御モードを使ってください(たとえば、URL パラメーターとの同期、外部のステートマシン、または before / after フックでは対応できない複雑な複数コンポーネント間の連携など)。
const [stepIndex, setStepIndex] = useState(0);
const [run, setRun] = useState(true);
const { Tour } = useJoyride({
continuous: true,
run,
stepIndex, // This makes it controlled
steps,
onEvent: (data) => {
const { action, index, status, type } = data;
if (([STATUS.FINISHED, STATUS.SKIPPED] as Status).includes(status)) {
setRun(false);
return;
}
if (type === 'step:after' || type === 'error:target_not_found') {
setStepIndex(index + (action === 'prev' ? -1 : 1));
}
},
});
制御モードのルール:
go()とreset()は無効になります(警告がログに記録されます)- イベントに応じて
stepIndexを更新する必要があります - ツアーは COMPLETE で一時停止します — 自分で次に進める必要があります
- インデックスを外部で管理する強い理由がない限り、
before/afterフックを使った非制御モードを推奨します
イベントシステム
onEvent コールバック
onEvent: (data: EventData, controls: Controls) => void
data オブジェクトには、ツアーの全状態とイベント固有のフィールドが含まれます。controls オブジェクトを使うと、ツアーをプログラムから制御できます。
イベントの種類(ステップごとの順序)
| イベント | タイミング |
|---|---|
tour:start | ツアー開始時 |
step:before_hook | before フックが呼び出されたとき |
step:before | 対象が見つかり、ステップを表示する直前 |
scroll:start | 対象へのスクロール開始時 |
scroll:end | スクロール完了時 |
beacon | ビーコン表示時 |
tooltip | ツールチップ表示時 |
step:after | ユーザーが移動したとき(次へ/前へ/閉じる/スキップ) |
step:after_hook | after フックが呼び出されたとき |
tour:end | ツアー終了またはスキップ時 |
tour:status | ステータス変更時(停止/リセット時) |
error:target_not_found | 対象の要素が見つからないとき |
error | 一般的なエラー |
on() によるイベントの購読
const { on, Tour } = useJoyride({ ... });
useEffect(() => {
const unsubscribe = on('tooltip', (data, controls) => {
analytics.track('tour_step_viewed', { step: data.index });
});
return unsubscribe;
}, [on]);
ドキュメント:https://v3.react-joyride.com/docs/events
コントロール
useJoyride() の戻り値、または onEvent の第 2 引数から利用できます。
| メソッド | 説明 |
|---|---|
next() | 次のステップに進む |
prev() | 前のステップに戻る |
close(origin?) | 現在のステップを閉じて、次に進む |
skip(origin?) | ツアー全体をスキップする |
start(index?) | ツアーを開始する |
stop(advance?) | ツアーを停止(一時停止)する |
go(index) | ステップに移動する(非制御モードのみ) |
reset(restart?) | ツアーをリセットする(非制御モードのみ) |
open() | 現在のステップのツールチップを開く |
info() | 現在の状態を取得する |
ドキュメント:https://v3.react-joyride.com/docs/hook
スタイル設定とテーマ設定
カスタマイズには、簡単なものから完全な制御まで 3 つの方法があります。
1. 色のオプション(最も簡単)
options: {
primaryColor: '#1976d2', // Buttons and beacon
backgroundColor: '#1a1a2e', // Tooltip background
textColor: '#ffffff', // Tooltip text
overlayColor: 'rgba(0,0,0,0.7)', // Overlay backdrop
arrowColor: '#1a1a2e', // Arrow (match background)
}
2. スタイルのオーバーライド
styles: {
tooltip: { borderRadius: 12 },
buttonPrimary: { backgroundColor: '#1976d2' },
buttonBack: { color: '#666' },
spotlight: { borderRadius: 8 },
}
スタイルキー:arrow、beacon、beaconInner、beaconOuter、beaconWrapper、buttonBack、buttonClose、buttonPrimary、buttonSkip、floater、loader、overlay、tooltip、tooltipContainer、tooltipContent、tooltipFooter、tooltipFooterSpacer、tooltipTitle
3. カスタムコンポーネント(完全な制御)
次のセクションを参照してください。
ドキュメント:https://v3.react-joyride.com/docs/props/styles
カスタムコンポーネント
props を使って任意の UI コンポーネントを置き換えられます。各コンポーネントには、ステップデータとボタンハンドラーを含むレンダープロップが渡されます。
カスタムツールチップ
import type { TooltipRenderProps } from 'react-joyride';
function MyTooltip({ backProps, index, primaryProps, size, skipProps, step, tooltipProps }: TooltipRenderProps) {
return (
<div {...tooltipProps} style={{ background: '#fff', padding: 16, borderRadius: 8, width: step.width }}>
{step.title && <h3>{step.title}</h3>}
<div>{step.content}</div>
<div>
{index > 0 && <button {...backProps}>Back</button>}
<button {...primaryProps}>Next</button>
</div>
</div>
);
}
// Usage
<Joyride tooltipComponent={MyTooltip} ... />
重要:コンテナに tooltipProps を展開してください(role="dialog" と aria-modal が設定されます)。正しくアクションを処理するには、ボタンにボタン用 props(backProps、primaryProps、closeProps、skipProps)を展開してください。
カスタムビーコン
<button> ラッパー内に配置される <span> をレンダリングする必要があります。BeaconRenderProps を受け取ります:{ continuous, index, isLastStep, size, step }。
カスタム矢印
ArrowRenderProps を受け取ります:{ base, placement, size }。
カスタムローダー
LoaderRenderProps を受け取ります:{ step }。ローダーを無効にするには null に設定します。
ドキュメント:https://v3.react-joyride.com/docs/custom-components
問題 → 解決方法ガイド
| 必要なこと | 使用するもの |
|---|---|
| ステップ間の非同期 UI 変更(ドロップダウン、アニメーション、データ読み込み)を待つ | Promise を返す before フック。制御モードは使用しない |
| ステップの移動を外部から制御する(URL 同期、外部ステートマシン) | stepIndex を使った制御モード。ただし、まず before/after フックを試してください |
| 失敗したステップ(対象が見つからない、フックエラー)を追跡する | useJoyride() の戻り値に含まれる failures 配列 |
onEvent の switch を使わずに特定のイベントをリッスンする | useJoyride() の戻り値から on('event:type', handler) を使用 |
| 中央揃えのモーダル形式のステップを表示する | target: 'body' + placement: 'center' |
よくある注意点とデバッグ
まず debug: true を使う
debug prop は、最も強力なトラブルシューティングツールです。ライフサイクルの遷移、状態の変更、イベントの発行について詳細なログをコンソールに出力します。問題が発生した場合は、必ずここから始めてください。
<Joyride debug={true} ... />
// or
useJoyride({ debug: true, ... })
コンソール出力には、ツアーがどのライフサイクルフェーズまで進んだか、どのアクションが実行されているか、どこで停止しているかが正確に表示されます。
ツアーが開始されない
run={true}が設定されていることを確認するsteps配列が空でなく、各ステップに有効なtargetとcontentがあることを確認する- SSR の場合:
<Joyride>コンポーネントを使用する (DOM アクセスを自動でガードします)、またはtypeof window !== 'undefined'を確認する
ターゲットが見つからない
- コンソールでセレクターをテストする:
document.querySelector('.your-selector') - 要素が表示されている必要があります (
display: none、visibility: hidden、またはサイズがゼロであってはなりません) - 要素が後からマウントされる場合は、
targetWaitTimeoutを増やします (デフォルトは 1000ms) targetWaitTimeout: 0を設定すると待機を完全にスキップします- 非制御モードでは、ターゲットが見つからない場合は自動的に次へ進みます。制御モードでは、
error:target_not_foundイベントを処理してください
ツールチップが表示されない / オーバーレイがちらつく
debug: trueを追加し、コンソールでどのライフサイクルフェーズに到達しているかを確認する- ターゲット要素がビューポート内にあるか、スクロール可能であることを確認する
- 祖先要素の CSS
overflow: hiddenがターゲットをクリップしていないか確認する - ポータルやモーダルを使用している場合、ターゲットにアクセスできない可能性があります
制御モードでスタックする
- 最初の疑問: 本当に制御モードが必要ですか? 非同期のニーズのほとんどは、非制御モードの
before/afterフックで解決できます - 制御モードを使用する場合:
type === 'step:after'のときにonEventハンドラーでstepIndexを必ず更新してください - 前方ナビゲーション (
action !== 'prev') と後方ナビゲーション (action === 'prev') の両方を処理してください - 壊れたステップをスキップするために
error:target_not_foundも処理してください go()とreset()は制御モードでは動作しません
before フックのタイムアウト
- デフォルトの
beforeTimeoutは 5000ms です — 非同期操作にそれ以上かかる場合は増やしてください beforeTimeout: 0を設定するとタイムアウトしません- 待機中は
loaderDelay(300ms) の後にローダーが表示されます
スクロールの問題
scrollTargetを使用すると、ツールチップのターゲットとは別の要素にスクロールできます- ヘッダーや固定要素のために
scrollOffset(デフォルトは 20px) を調整します - ステップの自動スクロールを無効にするには
skipScroll: trueを設定します scrollToFirstStepはデフォルトでfalseです — 最初のステップが画面外にある場合はtrueに設定してください
中央配置
- モーダルスタイルの中央配置ツールチップには、
target: 'body'とともにplacement: 'center'を使用します - 中央配置ではビーコンと矢印が自動的に非表示になります
インポート
// Named exports only (no default export in v3)
import { Joyride, useJoyride } from 'react-joyride';
// Constants for type-safe comparisons
import { ACTIONS, EVENTS, LIFECYCLE, ORIGIN, STATUS } from 'react-joyride';
// Types
import type { Step, Props, EventData, Controls, TooltipRenderProps } from 'react-joyride';
ドキュメント: https://v3.react-joyride.com/docs/exports
リファレンスファイル
完全な API の詳細については、以下をお読みください:
references/api-props-options.md— Props、Options (デフォルト値付きの全 30 以上のフィールド)、Locale、FloatingOptions、Styles 型の全体references/api-step-state-controls.md— Step、StepMerged、StepTarget、State、Controls (全 10 メソッド)、UseJoyrideReturn、StepFailurereferences/api-events-components.md— 全 13 種類のイベント、ACTIONS/LIFECYCLE/STATUS/ORIGIN 定数、EventData、カスタムコンポーネントの render propsreferences/patterns.md— 完全な動作例: 制御モード、before/after フック、カスタムツールチップ、イベント購読、動的ステップ