diff --git a/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/en.ftl b/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/en.ftl
new file mode 100644
index 00000000000..50a272ea77f
--- /dev/null
+++ b/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/en.ftl
@@ -0,0 +1,12 @@
+## PagePasskeyAdd - Loading page shown during passkey creation
+
+page-passkey-add-creating-heading = Creating passkey…
+page-passkey-add-follow-prompts = Follow the prompts on your device.
+page-passkey-add-cancel = Cancel
+
+## Success / Error messages (shown in alert bar after returning to settings)
+
+page-passkey-add-success = Passkey created
+page-passkey-add-error-system = System not available. Try again later.
+
+##
diff --git a/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.stories.tsx b/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.stories.tsx
new file mode 100644
index 00000000000..21395e4cffa
--- /dev/null
+++ b/packages/fxa-settings/src/components/Settings/PagePasskeyAdd/index.stories.tsx
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import React from 'react';
+import { Meta } from '@storybook/react';
+import { withLocalization } from 'fxa-react/lib/storybooks';
+import { LocationProvider } from '@reach/router';
+import { PagePasskeyAdd } from '.';
+import { AppContext } from 'fxa-settings/src/models';
+import {
+ mockAppContext,
+ mockSettingsContext,
+} from 'fxa-settings/src/models/mocks';
+import { SettingsContext } from 'fxa-settings/src/models/contexts/SettingsContext';
+import { MfaContext } from '../MfaGuard';
+import { getDefault } from '../../../lib/config';
+
+export default {
+ title: 'Pages/Settings/PasskeyAdd',
+ component: PagePasskeyAdd,
+ decorators: [withLocalization],
+} as Meta;
+
+const mockAccount = {
+ getCachedJwtByScope: () => 'mock-jwt',
+} as any;
+
+// Auth client that never resolves — keeps the loading page visible.
+const hangingAuthClient = {
+ beginPasskeyRegistration: () => new Promise(() => {}),
+ completePasskeyRegistration: () => new Promise(() => {}),
+};
+
+function initLocalAccount() {
+ const NS = '__fxa_storage';
+ const uid = 'abc123';
+ const accounts = {
+ [uid]: {
+ uid,
+ sessionToken: 'mock-session-token',
+ email: 'user@example.com',
+ verified: true,
+ lastLogin: Date.now(),
+ },
+ };
+ window.localStorage.setItem(`${NS}.accounts`, JSON.stringify(accounts));
+ window.localStorage.setItem(`${NS}.currentAccountUid`, JSON.stringify(uid));
+}
+
+const configWithPasskeys = {
+ ...getDefault(),
+ featureFlags: {
+ ...getDefault().featureFlags,
+ passkeyRegistrationEnabled: true,
+ },
+};
+
+export const CeremonyInProgress = () => {
+ initLocalAccount();
+ return (
+
+ Follow the prompts on your device. +
+