User Settings and Profile Management in Makerkit Next.js Prisma

Understand Makerkit's settings architecture and manage user profiles and organization settings. By the end, you'll know how to customize settings pages and manage team members.

In this module, you'll explore how Makerkit organizes settings pages and learn to manage user profiles and organization settings.

Technologies used:

  • Better Auth - User and organization management
  • React Hook Form - Form handling
  • next-safe-action - Server actions

What you'll accomplish:

  • Understand the settings architecture
  • Learn the personal vs organization settings distinction
  • Update profile settings
  • Manage team members and invitations
  • Know how to extend settings functionality

Understanding Settings Architecture

Makerkit organizes settings into two contexts: Personal Account settings and Organization settings. The system automatically detects which context you're in - so if you're logged into an organization, you will see the relative organization's settings. The profile settings are always visible.

Settings Page Hierarchy

URLContextPurpose
/settingsPersonalProfile (name, image, deletion)
/settings/securityPersonalPassword, email, MFA, OAuth
/settings/preferencesPersonalTheme, language
/settings/organizationOrganizationName, logo, deletion
/settings/membersOrganizationMembers and invitations
/settings/billingBothSubscriptions (context-dependent)

The Settings Layout

All settings pages share a common layout with a sidebar navigation:

apps/web/app/[locale]/(internal)/settings/layout.tsx

import { SettingsSidebar } from './_components/settings-sidebar';
export default function SettingsLayout({ children }: React.PropsWithChildren) {
return <SettingsSidebar>{children}</SettingsSidebar>;
}

The SettingsSidebar component dynamically builds navigation based on the current context (personal or organization).

Account Context Detection

Server components detect the account context to show appropriate settings:

import { getAccountContext } from '@kit/better-auth/context';
// In a server component
const ctx = await getAccountContext();
if (ctx.isPersonal) {
// Show personal account settings
} else if (ctx.isOrganization) {
// Show organization settings
}

Personal Account Settings

Personal settings manage the user's account across all organizations.

Profile Page (/settings)

The profile page includes:

  • Display Name - User's visible name
  • Profile Image - Avatar upload
  • Account Deletion - Delete entire account

Security Page (/settings/security)

The security page varies based on authentication method:

FeatureCredential UsersOAuth-Only Users
Change PasswordYesNo
Change EmailYesNo
Enable MFAYesNo
OAuth AccountsLink moreManage existing

NB: MFA is only applies to users who signed in with email and password. Users who signed in with OAuth providers are not required to have MFA enabled (it is expected they use the OAuth provider's own MFA system).

Preferences Page (/settings/preferences)

Users can set:

  • Theme - Light, dark, or system
  • Language - Available locales

Organization Settings

Organization settings are scoped to the current organization and require appropriate roles.

Organization Profile (/settings/organization)

FeatureOwnerAdminMember
Update nameYesYesNo
Update logoYesYesNo
Delete organizationYesNoNo

Organization Deletion

Deletion has eligibility requirements:

  1. Must be the owner
  2. No active subscriptions (must cancel first)
  3. User must verify via email OTP

The deletion card shows warnings before allowing deletion:

// Delete card checks eligibility before rendering
const eligibility = await checkOrganizationDeletionEligibility({
organizationId: orgId,
userId,
logger: { silent: true },
});
if (!eligibility.canDelete) {
// Show warnings based on eligibility.blockedReasons
// e.g., blockedReasons.activeSubscriptions
}

Checkpoint: Explore Settings Pages

Let's explore what's available by default.

Step 1: Personal Settings

  1. Sign in to your app
  2. Click your avatar in the top right
  3. Select Account Settings or go to /settings
  4. You should see:
    • Your profile section (name, image)
    • Account deletion option

Step 2: Security Settings

  1. Go to /settings/security
  2. Depending on how you signed up, you'll see:
    • Password change (if you have a password)
    • Email change
    • MFA settings
    • Connected OAuth accounts

Step 3: Organization Settings

  1. Go to /settings/organization
  2. You should see:
    • Organization name editor
    • Organization logo upload
    • Danger zone (deletion)

Step 4: Members Page

  1. Go to /settings/members
  2. You should see:
    • Current members list with roles
    • Pending invitations (if any)
    • Invite member button

Hands-On: Update Profile Settings

Let's trace through how a profile update works.

Step 1: Update Your Display Name

  1. Go to /settings
  2. Find the "Display Name" section
  3. Change your name to something different
  4. Click Save

You should see a success toast. The change is reflected immediately.

Step 2: Understand the Flow

The update follows this pattern:

Form Component (client)
React Hook Form + Zod Validation
Server Action (authenticated)
Better Auth API (auth.api.updateUser)
Database Update
Path Revalidation
UI Updates

Hands-On: Invite & Manage Members

Step 1: Invite a New Member

  1. Go to /settings/members
  2. Click Invite Member
  3. Enter an email address
  4. Select a role (try Admin)
  5. Click Send Invitation

The invitation appears in the "Pending Invitations" section.

Step 2: View the Invitation Email

  1. Open Mailpit at http://localhost:8025
  2. Find the invitation email
  3. Note the invitation link format: /accept-invitation/{invitationId}

Step 3: Manage Existing Members

If you have the completed the invitation flow from Module 5:

  1. Find the member in the members list
  2. Click the ... (actions) menu
  3. You can:
    • Update Role - Change their role (if you outrank them)
    • Remove - Remove them from the organization

Step 4: Cancel an Invitation

  1. Find a pending invitation
  2. Click the ... (actions) menu
  3. Click Cancel Invitation
  4. Confirm the cancellation

Member Actions Summary

ActionOwnerAdminMember
Invite membersYes (any role)Yes (admin or lower)Yes (member only)
Update rolesYes (any)Yes (not owners)No
Remove membersYes (any)Yes (not owners)No
Cancel invitationsYesYesNo

What Else Is Possible

MFA/Two-Factor Authentication Setup

The MFA setup is a multi-step wizard:

  1. Password Verification - Confirm identity
  2. QR Code Display - Scan with authenticator app
  3. OTP Verification - Enter code from app
  4. Backup Codes - Save recovery codes
// Using the hook (client-side):
import { useEnableTwoFactor } from '@kit/auth/hooks/use-enable-two-factor';
const enableTwoFactor = useEnableTwoFactor();
// Trigger MFA enrollment:
const result = await enableTwoFactor.mutateAsync({
password: userPassword,
});
// Returns:
// - totpURI: QR code data for authenticator apps (note: uppercase URI)
// - backupCodes: One-time recovery codes

OAuth Account Linking

Users can link multiple OAuth providers using dedicated hooks:

import { useLinkAccount } from '@kit/auth/hooks/use-link-account';
import { useUnlinkAccount } from '@kit/auth/hooks/use-unlink-account';
// Link a new provider
const { linkAccount, isPending, error } = useLinkAccount();
await linkAccount({ provider: 'google', callbackURL: '/settings' });
// Unlink a provider (uses react-query mutation)
const unlinkAccount = useUnlinkAccount();
await unlinkAccount.mutateAsync({ provider: 'google' });

Email Change Flow

Email changes require verification:

  1. User submits new email (must confirm)
  2. Verification link sent to current email (security)
  3. User clicks link to confirm change
  4. Email updated in database

Password Requirements

Password changes validate:

  • Current password (for verification)
  • New password (minimum 8 characters)
  • Confirmation (must match)
  • Must be different from current password

Account Deletion Eligibility

Before deletion, the system checks:

const eligibility = await checkAccountDeletionEligibility({
userId,
logger: { silent: true },
});
// Returns:
// - canDelete: boolean (whether deletion is allowed)
// - blockedReasons: {
// activeSubscriptions: Subscription[],
// blockedOrganizations: Organization[],
// }
// - organizationsToDelete: Organization[]

Adding Custom Settings Pages

To add a new settings page:

  1. Create the page in apps/web/app/[locale]/(internal)/settings/your-page/page.tsx
  2. Add to the sidebar navigation in settings-sidebar.tsx
  3. Follow the card + form pattern for consistency
// Example: Custom integrations page
export default async function IntegrationsPage() {
const integrations = await loadUserIntegrations();
return (
<div className="space-y-6">
<PageHeader
title="Integrations"
description="Connect external services"
/>
<IntegrationsList integrations={integrations} />
</div>
);
}

Module 10 Complete!

You now have:

  • [x] Understanding of settings architecture (personal vs organization)
  • [x] Knowledge of profile management patterns
  • [x] Experience with member management
  • [x] Awareness of security features (MFA, OAuth, email/password)
  • [x] Knowledge of how to extend settings

Next: In Module 11: Admin Dashboard, you'll explore the admin dashboard for platform management.


Learn More