Skip to content

user-profile

Self-service account page (fw#2312: declarative projectionDetail bound to the signed-in user’s own me row) plus its change-email write handler (re-auth via current password, uniqueness check, resets emailVerified and expects the app to trigger the verification flow). Change-password and change-email stay custom EditExtensionSection components (re-auth flows a declarative action can’t express); account deletion (user-data-rights request/cancel with grace period) is declarative fields + actions. Apps place the screen (id “profile”) in their logged-in area via r.nav — no type: "custom" / __component registration needed on the app side anymore. Requires user, auth-email-password, user-data-rights, and user-data-rights-defaults (so the GDPR boot-validator finds export/delete hooks for user’s PII fields once user-data-rights is mounted).

From recipes-user-profile — the smallest working mount:

// user-profile Recipe — shows the app-side wiring for the self-service
// account page: the bundled feature ships the screen itself (fw#2312:
// declarative `projectionDetail`, id "profile") including handler + i18n;
// the app only has to navigate to it, no own `r.screen()` registration
// anymore (unlike before #2312 — see user-data-rights' recipe/demo pattern:
// `r.nav({ screen: "<feature>:screen:<id>", ... })`).
//
// Client-side, the app only registers the two extension-section components
// (change-password/change-email stay React, re-auth):
// import { userProfileClient } from
// "@cosmicdrift/kumiko-bundled-features/user-profile/web";
// createKumikoApp({
// clientFeatures: [emailPasswordClient(), userProfileClient()],
// })
import { createAuthEmailPasswordFeature } from "@cosmicdrift/kumiko-bundled-features/auth-email-password";
import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
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 { createPersonalAccessTokensFeature } from "@cosmicdrift/kumiko-bundled-features/personal-access-tokens";
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: just the nav " +
"entry — user-profile registers the `profile` screen itself " +
"(fw#2312 declarative projectionDetail).",
);
r.requires("user-profile");
r.nav({
id: "profile",
label: "account:nav.profile",
icon: "user",
screen: "user-profile: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(),
authFoundationFeature,
createPersonalAccessTokensFeature({ scopes: {} }),
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

user-profile feature preview

What this feature needs to run (Requires, top) and the write commands this feature provides (Provides, bottom).

flowchart TB
  n_user_profile["user-profile"]
  subgraph how_reqs["Requires"]
    n_user["user"]
    n_auth_email_password["auth-email-password"]
    n_user_data_rights["user-data-rights"]
    n_user_data_rights_defaults["user-data-rights-defaults"]
  end
  subgraph how_provides["Provides"]
    n_cmd_user_profile_write_change_email(["change-email"])
  end
  n_user --> n_user_profile
  n_auth_email_password --> n_user_profile
  n_user_data_rights --> n_user_profile
  n_user_data_rights_defaults --> n_user_profile
  n_user_profile --> n_cmd_user_profile_write_change_email

Provides — write commands this feature registers (dispatch them through the command bus):

Start with recipes-user-profile for a step-by-step walkthrough with runnable code and integration tests.