Skip to content

user-profile

Self-service account page as a bundled feature: change password, change email (with re-auth + verification reset), and request or cancel account deletion (with grace period from user-data-rights).

The bundled feature ships handlers, ProfileScreen, and i18n. Your app declares the screen in nav and registers the React component — this recipe shows the wiring.

  • user-profile:write:change-email — re-authenticated email change; resets emailVerified until the user confirms the new address.
  • auth-email-password:write:change-password — used from the same screen; lives on auth-email-password, not duplicated in user-profile.
  • Account deletionuser-data-rights handlers for request / cancel deletion; grace period comes from the tenant’s compliance profile.
  • App-side screen declarationr.screen({ type: "custom", renderer: { react: { __component: "UserProfileScreen" } } }) plus matching components map on the client.
  • Full require chainusertenantauth-email-passworduser-data-rights (which pulls data-retention, compliance-profiles, sessions). composeAccountApp() mounts everything boot needs.
user → cross-tenant identity (email, roles, status)
tenant → memberships + tenant-scoped roles
auth-email-password → login + change-password handlers
data-retention → retention policies (via user-data-rights)
compliance-profiles → region profiles for grace periods
sessions → revocable JWTs (via user-data-rights chain)
user-data-rights → deletion request / export pipeline
user-profile → change-email handler + ProfileScreen component
account → this recipe: screen + nav wiring only

Register the bundled screen once at app boot:

import { ProfileScreen, userProfileClient } from "@cosmicdrift/kumiko-bundled-features/user-profile/web";
import { emailPasswordClient } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/web";
import { createKumikoApp } from "@cosmicdrift/kumiko-renderer-web";
createKumikoApp({
components: { UserProfileScreen: ProfileScreen },
clientFeatures: [emailPasswordClient(), userProfileClient()],
// ... schema, nav from server features
});

The __component: "UserProfileScreen" string on the server screen must match the key in components.

  1. User opens the profile nav entry → renderer loads ProfileScreen.
  2. Password change dispatches auth-email-password:write:change-password.
  3. Email change dispatches user-profile:write:change-email (re-auth required).
  4. Delete account dispatches user-data-rights:write:request-deletion; cancel uses the matching cancel handler during the grace window.

Boot validation only — no DB/HTTP in this recipe (HTTP proof lives in the bundled-feature integration tests):

Terminal window
bun test src/__tests__/feature.test.ts

The test asserts validateBoot(composeAccountApp()) passes, the profile screen/nav are registered, and the documented change-email qualified name matches UserProfileHandlers.changeEmail.


The feature entry point — embedded straight from the source file, so the code here is exactly what runs. Multi-file samples keep their remaining files next to it on GitHub (link below):

// user-profile Recipe — zeigt das App-Wiring für die Self-Service-
// Kontoseite: das bundled feature liefert Handler (change-email) +
// ProfileScreen-Komponente + i18n; die App deklariert den Screen als
// `custom` mit der __component-Convention und hängt ihn in die Nav.
//
// Client-seitig registriert die App die Komponente im Renderer-Mount:
// import { ProfileScreen, userProfileClient } from
// "@cosmicdrift/kumiko-bundled-features/user-profile/web";
// createKumikoApp({
// components: { UserProfileScreen: ProfileScreen },
// clientFeatures: [emailPasswordClient(), userProfileClient()],
// })
import { createAuthEmailPasswordFeature } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
import { createDataRetentionFeature } from "@cosmicdrift/kumiko-bundled-features/data-retention";
import { createFilesFeature } from "@cosmicdrift/kumiko-bundled-features/files";
import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";
import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant";
import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";
import { createUserDataRightsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
import { createUserDataRightsDefaultsFeature } from "@cosmicdrift/kumiko-bundled-features/user-data-rights-defaults";
import { createUserProfileFeature } from "@cosmicdrift/kumiko-bundled-features/user-profile";
import { defineFeature, type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
export function createAccountFeature(): FeatureDefinition {
return defineFeature("account", (r) => {
r.describe(
"App-side wiring for the user-profile bundled feature: declares the " +
"profile screen (custom renderer, __component UserProfileScreen) and " +
"its nav entry.",
);
r.requires("user-profile");
r.screen({
id: "profile",
type: "custom",
renderer: { react: { __component: "UserProfileScreen" } },
});
r.nav({
id: "profile",
label: "account:nav.profile",
screen: "account:screen:profile",
order: 90,
});
return {};
});
}
/** Volle Feature-Komposition inkl. der user-profile-Require-Kette
* (user-data-rights → data-retention + compliance-profiles + sessions). */
export function composeAccountApp(): FeatureDefinition[] {
return [
createConfigFeature(),
createUserFeature(),
createTenantFeature(),
createAuthEmailPasswordFeature(),
createDataRetentionFeature(),
createComplianceProfilesFeature(),
createSessionsFeature(),
createFilesFeature(),
createUserDataRightsFeature(),
// registers the default export/erase hooks for core PII entities (user,
// fileRef, folder) so the GDPR boot gate (V3) is satisfied for the stack.
createUserDataRightsDefaultsFeature(),
createUserProfileFeature(),
createAccountFeature(),
];
}

📄 On GitHub: samples/recipes/user-profile/src/feature.ts