アプリのオンボーディングは、説明画面を何枚作るかより、いつ表示し、いつ権限を求め、何を計測するかで成果が決まります。この記事では、ウォークスルー・コーチマーク・段階表示の使い分けから、初回起動の判定コード、driver.js 1.10系でのツアー実装、Android 13以降の通知権限とiOSの仮許可の要求タイミング、GA4の推奨イベントを使った完了率の計測までを、2026年11月2日時点の公式ドキュメントに沿って整理する内容です。オンボーディングUIを作り込むべきアプリと、画面そのものを直すべきアプリの線引きも示します。
まとめ:アプリのオンボーディングで実装前に決める表示条件と権限の時機
先に決めるのは3つではなく2つです。1つ目は「誰に、どの版のツアーを、何回出すか」という表示条件で、端末をまたいで使うサービスならサーバー側に完了状態を持ちます。2つ目は権限を求める時機です。通知や位置情報の許可は初回起動で出さず、その権限が要る操作をユーザーが始めた直後に求めます。Androidでは2回拒否されると以後ダイアログが出なくなるため、起動直後の要求は取り返しがつきません。
計測はGA4の推奨イベントtutorial_beginとtutorial_completeを基準に置き、ステップごとの離脱を独自イベントで補います。ただし効果判定はツアーの完了率でなく、初回の主要操作に到達した割合で行います。ツアーで画面の分かりにくさを補おうとしている場合は、オンボーディングを足すより画面を直すほうが先です。
アプリのオンボーディングUIを構成するウォークスルー・コーチマーク・段階表示の違い
オンボーディングUIは見た目の名前が多いものの、実装上は「別画面で説明する」「実画面の上に重ねる」「操作に応じて少しずつ見せる」の3系統に分かれます。どれを選ぶかで、必要な状態管理と計測の粒度が変わります。
ウォークスルー型の初回画面が向くアプリと枚数を増やしたときの離脱
ウォークスルーは、初回起動時に横スワイプのカードを数枚見せる方式です。アプリの価値を言葉と画像で伝えるだけなので、実装は画面遷移と「スキップ」ボタン、完了フラグの保存で済みます。サブスクリプション型の消費者向けアプリのように、使い始める前に価値を納得させる必要がある場面に向いています。
弱点は、実際の操作を何も教えない点です。カードを増やすほど読まれずにスキップされるため、操作の説明はコーチマークか段階表示に回します。
コーチマークで実画面の操作箇所を示す方式とツールチップとの使い分け
コーチマークは、実際の画面を暗くして特定のボタンだけを強調し、吹き出しで操作を説明する方式です。Web製品ではプロダクトツアーとも呼ばれ、driver.jsやreact-joyrideといったライブラリで組めます。ボタンの位置と役割を同時に示せるため、管理画面や業務アプリのように画面要素が多い製品で効きます。
ツールチップは、強調せずに小さな説明を1つ添えるだけの軽い方式です。操作の順番を教えたいならコーチマーク、単発の補足ならツールチップと使い分けます。
プログレッシブ開示と空状態の画面で初回操作へ誘導する設計例と3方式の比較
プログレッシブ開示は、最初は基本機能だけを見せ、操作が進んだ段階で次の機能を案内する方式です。「どの機能を案内済みか」をユーザー単位で持つため、3系統の中で状態管理が最も重くなります。
空状態(データが1件も無い画面)は、最も効く誘導の場所です。案件管理アプリなら、空の一覧に「最初の案件を登録する」ボタンと1行の説明を置くだけで、ツアーを出さずに初回操作へ誘導できます。3方式の違いを表にまとめます。
| 方式 | 向く場面 | 保存する状態 | 計測の単位 |
|---|---|---|---|
| ウォークスルー | 価値の説明が先に要る消費者向けアプリ | 完了フラグ1つ | カードごとの表示とスキップ |
| コーチマーク | 画面要素が多い管理画面・業務アプリ | ツアーの版と完了状態 | ステップごとの表示と離脱 |
| 段階表示・空状態 | 機能が多く一度に説明しきれない製品 | 案内済みの機能の一覧 | 機能ごとの初回利用 |
実務ではまず空状態の画面を整え、それでも迷う操作が残る箇所にだけコーチマークを足す順番にすると、ツアーの本数が増えすぎません。
初回起動の判定をlocalStorageとサーバー側のテーブルで実装する手順
完了の記録先は、1台の端末だけで使うか、複数の端末から同じアカウントで使うかで決めます。
Webアプリで版番号付きのキーをlocalStorageに保存する初回判定コード
ログイン不要のWebアプリや、端末をまたがないPWAなら、ブラウザのlocalStorageで足ります。キー名に版番号を入れておくと、ツアーの内容を変えたときに版を上げるだけで全員に再表示できます。
// onboarding-state.js
const ONBOARDING_KEY = "onboarding:v2"; // ツアー内容を変えたら v3 に上げる
export function shouldShowOnboarding() {
try {
return localStorage.getItem(ONBOARDING_KEY) === null;
} catch {
return false; // ストレージが使えない環境ではツアーを出さない
}
}
export function markOnboardingDone(result) {
// result は "completed" か "skipped"
localStorage.setItem(
ONBOARDING_KEY,
JSON.stringify({ result, at: new Date().toISOString() })
);
}
catchでfalseを返しているのは、プライベートブラウズなどでストレージの読み書きが失敗したときに、毎回ツアーが出続ける事故を防ぐためです。スキップも完了と同じく記録し、二度と自動では出しません。localStorageはブラウザのデータ削除で消え、同期処理でもあるため、容量や他の保存先との違いはIndexedDB・localStorage・Cookieの容量上限と使い分けで確認してください。
複数端末で使うサービスはサーバー側に完了日時を持つテーブル設計
スマホアプリとWeb版の両方で使う業務システムでは、端末ごとの記録だと、スマホで完了したツアーがPCで再び出ます。ログインがあるサービスなら、完了状態の保存先はユーザー単位のサーバー側です。PostgreSQLなら次のようなテーブルで足ります。
CREATE TABLE user_onboarding (
user_id BIGINT NOT NULL,
flow_name VARCHAR(32) NOT NULL, -- 例: first_run、invoice_feature
flow_version SMALLINT NOT NULL,
status VARCHAR(16) NOT NULL, -- completed または skipped
last_step SMALLINT NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, flow_name, flow_version)
);
flow_nameを主キーに含めると、新機能の案内を初回ツアーと分けて管理でき、該当行が無いユーザーにだけ差分のツアーを出せます。last_stepは離脱位置の記録で、サポート窓口が途中でやめた利用者を抽出するときにも使える項目です。Androidのネイティブアプリで端末内に持つ場合は、JetpackのDataStoreにキーと値で保存します。
driver.js 1.10系でコーチマーク型のツアーを組む導入から終了処理まで
WebアプリでコーチマークをゼロからCSSで作ると、強調の切り抜き、スクロール追従、画面サイズ変更への対応だけで数日かかります。ここでは依存の少ないdriver.jsを例に、ツアーの定義と終了時の記録までを組みます。
driver.jsと代替ライブラリのライセンス・版番号と選定の条件
npm registryで確認すると、driver.jsの最新は1.10.2(2026年10月18日公開)でMITライセンスです。GitHubのリポジトリは2026年11月時点でnilbuildへ移っており、READMEでは依存ライブラリなし・約5kb(gzip後)と説明されています。同じ用途のライブラリは、ライセンスが分かれる点に注意が要ります。
| ライブラリ | 2026-11-02時点の最新 | npm上のライセンス | 向く構成 |
|---|---|---|---|
| driver.js | 1.10.2 | MIT | フレームワークを問わないWeb |
| react-joyride | 3.2.0 | MIT | Reactの画面 |
| shepherd.js | 15.3.0 | AGPL-3.0 | ライセンス条件を確認できる案件 |
| intro.js | 8.6.0 | AGPL-3.0 | ライセンス条件を確認できる案件 |
AGPL-3.0のライブラリを受託案件の商用サービスに組み込むと、ソースコードの開示義務を負うかどうかの確認が発注側の法務まで及びます。版と条件は変わるため、採用前に各パッケージの最新のライセンス表記を見てください。特別な理由が無ければ、MITのdriver.jsかreact-joyrideから選ぶと契約上の確認が軽く済みます。
4ステップのツアー定義と途中で閉じられたときを記録するコード
公式のインストール手順どおりnpm install driver.jsで入れ、stepsにステップを並べてdrive()で開始します。下のコードは、前章の初回判定とGA4の計測を組み合わせたものです。
import { driver } from "driver.js";
import "driver.js/dist/driver.css";
import { shouldShowOnboarding, markOnboardingDone } from "./onboarding-state.js";
const FLOW_VERSION = 2;
// 画面に存在しない要素のステップは外す(権限や設定で非表示のボタンがあるため)
const steps = [
{ element: "#new-project", popover: { title: "案件を登録", description: "最初の案件はここから作ります。" } },
{ element: "#member-invite", popover: { title: "メンバーを招待", description: "担当者を追加すると通知が届きます。" } },
{ element: "#status-filter", popover: { title: "進捗で絞り込む", description: "遅れている案件だけを表示できます。" } },
{ element: "#help-menu", popover: { title: "あとで見返す", description: "このツアーはヘルプからいつでも再生できます。" } },
].filter((s) => document.querySelector(s.element));
const tour = driver({
showProgress: true,
steps,
onHighlighted: (el, step, { index }) => {
gtag("event", "onboarding_step_view", { step_index: index, flow_version: FLOW_VERSION });
},
onDestroyStarted: (el, step, { index }) => {
const completed = tour.isLastStep();
gtag("event", completed ? "tutorial_complete" : "onboarding_skip", {
step_index: index,
flow_version: FLOW_VERSION,
});
markOnboardingDone(completed ? "completed" : "skipped");
tour.destroy(); // onDestroyStarted を指定したときは自分で閉じる
},
});
if (steps.length > 0 && shouldShowOnboarding()) {
gtag("event", "tutorial_begin");
tour.drive();
}
onDestroyStartedの中でdestroy()を呼ぶ形は、driver.jsの設定ドキュメントの例と同じです。このコードでは最後のステップで閉じた場合を完了として数えます。最後のステップでも「×」で閉じた利用者を完了から外したい場合は、onNextClickで最後の「完了」ボタンだけを判定してください。
強調する要素の描画前にツアーが始まり吹き出しの位置が崩れる失敗と回避策
コーチマークで最も多い不具合は、強調する要素がまだ描画されていない時点でツアーを始めてしまうことです。APIの応答を待って一覧を描くSPAでは、ページ読み込み直後にdrive()を呼ぶと、吹き出しが画面の左上に出たり、ステップが飛ばされたりします。上のコードでfilterを掛けたのは、要素が無いステップを先に外すためです。
根本の対策は、ツアーの開始をデータ取得の完了後に置くことです。スマホ幅でサイドバーが畳まれる画面では、ステップをPC用とスマホ用に分けます。ステップの要素が存在するかをE2Eテストで確かめておけば、ボタンのIDが変わってツアーだけ壊れる事故を拾えます。
キーボード操作とフォーカスの戻し先をW3CのDialog Patternで点検
コーチマークは画面の他の部分を操作できなくするため、アクセシビリティ上はモーダルダイアログとして扱います。W3CのDialog (Modal) Patternは、開いたときにフォーカスをダイアログ内へ移す、TabとShift+Tabはダイアログ内で循環させる、Escapeで閉じる、閉じたら呼び出し元の要素へフォーカスを戻す、と定めています。
ライブラリ任せにせず、リリース前にこの4点をキーボードだけで確かめてください。role="dialog"やaria-modal="true"を自作の吹き出しに付ける場合の注意は、WAI-ARIAのrole・aria属性の使い方で解説しています。
通知・位置情報などの権限リクエストを初回起動で出さないためのタイミング設計
オンボーディングの中で通知や位置情報の許可を一気に求める作りは、許可率を下げるうえに、Androidでは再要求の機会まで失います。各OSの公式ドキュメントは、どれも「その権限が要る操作の文脈で求める」ことを推奨しています。
Android 13の通知権限をボタン操作の直後に求めるComposeのコード
Androidの通知権限のドキュメントによると、Android 13(API 33)以降の端末に新しく入れたアプリは、通知が既定でオフです。targetSdkを12L(API 32)以下のままにしていると、通知チャンネルを作った後の最初のActivity起動時、つまり多くは起動直後にシステムがダイアログを出してしまいます。表示の時機を自分で制御するには、targetSdkを33以上に上げる必要があります。
公式はよい時機の例として、ベルのボタンを押したとき、誰かをフォローしたとき、注文を確定したときを挙げています。Jetpack Composeでは次のように、操作のボタンから要求します。
@Composable
fun FollowButton(onFollow: () -> Unit) {
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
onFollow() // 許可・拒否のどちらでもフォロー自体は完了させる
logPermissionResult("POST_NOTIFICATIONS", isGranted) // 自前の計測関数
}
Button(onClick = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
onFollow()
}
}) {
Text("フォローして更新通知を受け取る")
}
}
拒否されてもフォローの処理は止めない点が要です。実行時権限の要求ガイドは、拒否されても機能を縮小してアプリを使い続けられるようにすること、全画面の警告で操作を塞がないことを原則に挙げています。Composeの状態管理そのものはJetpack Composeの宣言的UIの仕組みを参照してください。
2回拒否で再表示されないAndroidの仕様と説明画面を挟む条件
同じガイドには、インストール後に同じ権限を2回以上拒否されると、その後はシステムのダイアログが表示されなくなると書かれています。拒否1回目はUSER_SET、2回目でUSER_FIXEDのフラグが付き、以後は設定画面から変えてもらうしかありません。オンボーディングの最後で通知・位置情報・カメラを続けて求め、1つでも拒否されると、その権限は1回分を使い切ったことになります。
説明画面(なぜ権限が要るかを伝える自前の画面)を出すのは、shouldShowRequestPermissionRationale()がtrueを返したとき、つまり一度拒否された利用者が再びその機能を使おうとした場面だけで足ります。検証中に拒否状態を戻すときはadb shell pm clear-permission-flagsでフラグを消します。
iOSの通知の仮許可.provisionalと、Webの通知をクリック時に要求する実装
iOSのrequestAuthorizationは、最初の呼び出しでだけ許可のプロンプトを出し、以後は呼んでも出ません。Appleのドキュメントは、タスク管理アプリなら最初のタスクを登録した後に求める、という例を挙げています。起動直後に通知を使いたい場合の選択肢は、.provisionalを付けた仮許可です。仮許可の通知は音もバナーも出さず通知センターの履歴にだけ届き、利用者はそこで「残す」か「オフにする」を選びます。
let center = UNUserNotificationCenter.current()
do {
// ダイアログを出さずに、通知センターへ静かに届ける許可を得る
try await center.requestAuthorization(options: [.alert, .sound, .badge, .provisional])
} catch {
// 失敗しても起動処理は止めない
}
Webの通知は、MDNのNotification.requestPermission()のとおりHTTPSでのみ使え、ユーザー操作への応答として呼びます。戻り値はgranted・denied・defaultの3値です。拒否済みかどうかはPermissions APIのquery()で先に確かめます。
document.querySelector("#enable-alerts").addEventListener("click", async () => {
if (!("Notification" in window)) return;
const status = await navigator.permissions.query({ name: "notifications" });
if (status.state === "denied") {
showSettingsHelp(); // ブラウザ設定から戻す手順を案内する自前の関数
return;
}
const result = await Notification.requestPermission();
gtag("event", "permission_result", { permission: "notifications", result });
});
クリック時に限る理由は数字で示されています。web.devの権限のベストプラクティス(2024年6月更新)によると、Chromeのデスクトップで出る権限プロンプトの77%は利用者の操作の兆候なしに表示され、その許可率は12%でした。操作の後に出したプロンプトでは30%まで上がります。通知の送信側の仕組みはFCM(Firebase Cloud Messaging)の仕組みと送信方法にまとめています。
オンボーディングの計測イベントをGA4の推奨イベントで設計し到達率で評価
イベントは推奨イベントを軸に、ステップ単位と初回の主要操作を足して設計します。
tutorial_beginとtutorial_completeを軸にしたイベント設計表
GA4の推奨イベントには、オンボーディングの開始を表すtutorial_beginと完了を表すtutorial_completeがあり、どちらもパラメータの指定はありません。名前を独自に作らずこの2つを使うと、レポートの意味がチーム内でぶれません。ステップ単位の離脱や権限の結果は、推奨イベントに無いため独自イベントで足します。
| イベント名 | 種別 | 送る時機 | 主なパラメータ |
|---|---|---|---|
| sign_up | 推奨 | アカウント作成の完了時 | method |
| tutorial_begin | 推奨 | ツアーの表示開始 | なし |
| onboarding_step_view | 独自 | 各ステップの表示 | step_index、flow_version |
| onboarding_skip | 独自 | 途中で閉じたとき | step_index、flow_version |
| tutorial_complete | 推奨 | 最後のステップの完了 | なし(版を送るなら flow_version) |
| permission_result | 独自 | 権限ダイアログの結果 | permission、result |
| first_key_action | 独自 | 初回の主要操作の完了 | action_name |
flow_versionを必ず付けるのは、ツアーを改修した前後で離脱の位置を比べるためです。モバイルアプリでFirebase SDKから送る場合のイベント数やパラメータの上限は、Firebase AnalyticsとGA4の違いとイベント上限で確認してください。
ツアーの完了率でなく初回の主要操作への到達率で効果を判定する理由
ツアーの完了率は、ステップを減らすだけで上がります。4ステップを2ステップにすれば完了率は伸びますが、利用者が使えるようになったかどうかとは関係がありません。評価に使うのは、tutorial_beginを送った利用者のうち、一定期間内にfirst_key_action(案件管理なら最初の案件登録、チャットなら最初の送信)まで進んだ割合です。
ツアーを出す群と出さない群で到達率がほぼ同じなら、そのツアーは削ってよいと判断できます。
オンボーディングを作り込むべきアプリと作らずに画面を直すべき場面の判断
ここまでの実装は、どのアプリにも必要なわけではありません。
ツアーで画面の分かりにくさを隠す失敗とオンボーディングを見送る場面
コーチマークを入れるべきなのは、操作の順番そのものに業務知識が要る場合です。たとえば受発注システムで「取引先を登録しないと見積を作れない」といった依存関係があり、画面だけでは順番が伝わらないときは、ツアーや空状態の案内が効きます。設定項目の多いSaaSの管理画面も同様です。
反対に、ボタンの文言や配置が分かりにくいだけの画面にはオンボーディングを作りません。文言と配置を直すほうが早く、ツアーは画面改修のたびに壊れる保守対象を増やします。月に数回しか開かない社内の申請画面も、ツアーより画面内の常設ヘルプが向く場面です。ツアーを入れるかどうかの前に、認知的ウォークスルーの4つの質問で初回操作の詰まりどころを洗い出すと、直すべきが画面か案内かを切り分けられます。
社内で実装するか画面設計から外部に依頼するかの判断と依頼前の準備
実装の量だけを見れば、初回判定・ツアー・権限要求・計測は、この記事のコードを土台に既存画面へ足せる範囲に収まります。社内にフロントエンドの担当者がいて、計測を見て改修を回せる体制があるなら、内製で問題ありません。顧客企業ごとの導入支援(完了条件の定義や、人が伴走するか製品内ガイドで済ませるかの判断)は、カスタマーサクセスのオンボーディング設計の領分で、実装より先に決めておく事項です。
外部に頼むべきなのは、ツアーで補っている問題が画面設計そのものにある場合です。どの操作で離脱しているかの計測から画面構成の見直しまでを一続きで進めるなら、一創のUI/UXデザインのように設計と実装を同じチームで扱える体制が向いています。依頼の前に、現状の離脱が多い画面のスクリーンショット、GA4のファネル、問い合わせで多い質問の上位を揃えておくと、改修範囲の見積もりが早く固まります。
アプリのオンボーディング実装で開発チームと発注担当者からよくある質問
アプリのオンボーディングを実装するときに、開発チームや発注担当者から出やすい質問をまとめました。
アプリのオンボーディング画面は何枚までに抑えるべきですか?
価値を説明するウォークスルーなら2〜3枚が目安です。それ以上の説明が必要な場合は、枚数を増やすのではなく、操作の説明をコーチマークや空状態の案内に移します。枚数の正解は製品によって違うため、カードごとのスキップ率を計測し、多くの利用者が飛ばしているカードから削るのが確実です。スキップボタンは1枚目から必ず置きます。
人事で使うオンボーディングとアプリのオンボーディングは同じ意味ですか?
言葉は同じですが対象が違います。人事のオンボーディングは、新しく入社した社員が職場に慣れて戦力になるまでの受け入れの取り組みを指します。アプリのオンボーディングは、新しい利用者が製品の使い方を覚えて継続利用に至るまでの初回体験の設計です。人事側の意味と工程はオンボーディングとはで解説しています。
オンボーディングをもう一度見たい利用者にはどう対応しますか?
ヘルプメニューに「ツアーをもう一度見る」を置き、押されたら判定を無視してdrive()を呼びます。このときtutorial_beginには再生であることを示す独自パラメータを付け、初回の計測と混ぜないようにしてください。
React Nativeやスマホのネイティブアプリでもdriver.jsは使えますか?
driver.jsはブラウザのDOMを操作するライブラリのため、React NativeやSwiftUI、Jetpack Composeの画面では使えません。ネイティブでは、各フレームワーク向けのコーチマーク用ライブラリを使うか、半透明の重ね合わせと吹き出しを自前で描きます。WebViewの画面なら動きますが、ネイティブ部分と混在する画面では強調の位置がずれやすくなります。
通知の許可はオンボーディングの最後でまとめて求めてもよいですか?
勧めません。Androidでは同じ権限を2回拒否されるとダイアログが出なくなり、iOSでは最初の1回しかプロンプトが出ないため、文脈の無い場面で断られると取り返しがつきません。オンボーディングの最後では「通知を使うと何が届くか」を説明するだけにとどめ、実際の要求は通知が役立つ操作をした直後に行います。iOSで起動直後から届けたい場合は仮許可を使います。
関連記事
- カスタマーサクセスのオンボーディング設計|完了条件・タッチモデル・製品内ガイドの判断:製品内ガイドを入れるかどうか、完了条件をどう決めるかを扱う判断側の記事です
- UI/UXとは?UIとUXの違いとUIデザインで使う数値基準:ツアーで補う前に見直す画面設計の基準をまとめています
- 認知的ウォークスルーとは?4つの質問と進め方、ヒューリスティック評価との違い:初回操作のどこで利用者が詰まるかを事前に洗い出す手法です
- Firebase Analyticsとは|GA4との違い・できること・導入手順とイベント上限【2026年9月時点】:モバイルアプリからオンボーディングのイベントを送るときの前提です
- Jetpack Composeとは?宣言的UIの仕組みとBOM運用・採用判断を解説:Androidで権限要求のボタンを組む画面の土台です