stack/docs-mintlify/guides/apps/authentication/user-onboarding.mdx
Bilal Godil c91a23ee88 Hexclave rename PR5: rename stack*App local-variable convention
Step 5: rename lowercase local vars stackApp/stackServerApp/stackClientApp/
stackAdminApp -> hexclave* across SDK source, apps, examples, and docs-mintlify
(docs/ excluded). Public StackServerApp/StackClientApp classes and the
useStackApp hook are unchanged. typecheck + lint green.
2026-06-03 12:17:14 -07:00

124 lines
3.8 KiB
Plaintext

---
title: "User Onboarding"
description: "Implementing a user onboarding page and collecting information on sign-up"
sidebarTitle: Onboarding
---
Sometimes, you may want to collect additional information from users during sign-up, for example a real name or address.
The most straightforward approach is to redirect users to an onboarding page right after they sign up. However, this is not recommended for the following reasons:
1. Users can accidentally (or purposefully) close or navigate away from the page before completing the onboarding.
2. Redirect URLs may vary depending on the context. For instance, if a user is redirected to a sign-in page after trying to access a protected page, they'll expect to return to the original protected page post-authentication.
Instead, a more reliable strategy is to store an `onboarded` flag in the user's metadata and redirect users to the onboarding page if they haven't completed it yet.
## Example implementation
Let's say you have an onboarding page that asks for an address and stores it in the user's [metadata](/guides/going-further/user-metadata):
```jsx
export default function OnboardingPage() {
const user = useUser();
const router = useRouter();
const [address, setAddress] = useState('');
return <>
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
/>
<button onClick={async () => {
await user.update({
clientMetadata: {
onboarded: true,
address,
},
});
router.push('/');
}}>
Submit
</button>
</>
);
}
```
<Info>
While the above implementation offers a basic onboarding process, users can still skip onboarding by directly sending an API request to update the `clientMetadata.onboarded` flag. If you want to ensure that onboarding cannot be bypassed on the API level, you should create a server endpoint to validate and store the data, then save the `onboarded` flag in the `clientReadOnlyMetadata` on the server side after validation.
</Info>
Next, we can create a hook/function to check if the user has completed onboarding and redirect them to the onboarding page:
<Tabs>
<Tab title="Client Component">
```jsx
'use client';
import { useEffect } from 'react';
import { useUser } from '@hexclave/next';
import { useRouter } from 'next/navigation';
export function useOnboarding() {
const user = useUser();
const router = useRouter();
useEffect(() => {
if (!user.clientReadOnlyMetadata.onboarded) {
router.push('/onboarding');
}
}, [user]);
}
```
</Tab>
<Tab title="Server Component">
```jsx
import { hexclaveServerApp } from '@/stack/server';
import { redirect } from 'next/navigation';
export async function ensureOnboarded() {
const user = await hexclaveServerApp.getUser();
if (!user.clientReadOnlyMetadata.onboarded) {
redirect('/onboarding');
}
}
```
</Tab>
</Tabs>
You can then use these functions wherever onboarding is required:
<Tabs>
<Tab title="Client Component">
```jsx
import { useOnboarding } from '@/app/onboarding-hooks';
import { useUser } from '@hexclave/next';
export default function HomePage() {
useOnboarding();
const user = useUser();
return (
<div>Welcome to the app, {user.displayName}</div>
);
}
```
</Tab>
<Tab title="Server Component">
```jsx
import { ensureOnboarding } from '@/app/onboarding-functions';
import { hexclaveServerApp } from '@/stack/server';
export default async function HomePage() {
await ensureOnboarding();
const user = await hexclaveServerApp.getUser();
return (
<div>Welcome to the app, {user.displayName}</div>
);
}
```
</Tab>
</Tabs>