Implementing a user onboarding page and collecting information on sign-up

By default, Neon Auth collects information such as email addresses from OAuth providers. Sometimes, you may want to collect additional information from users during sign-up, for example a 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:

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>
    </>
  );
}

note

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.

Next, we can create a hook/function to check if the user has completed onboarding and redirect them to the onboarding page:

'use client';

import { useEffect } from 'react';
import { useUser } from '@stackframe/stack';
import { useRouter } from 'next/navigation';

export function useOnboarding() {
  const user = useUser();
  const router = useRouter();

  useEffect(() => {
    if (user && !user.clientMetadata?.onboarded) {
      router.push('/onboarding');
    }
  }, [user]);
}

To add an Onboarding page and guarantee users hit it, create a dedicated /onboarding page and gate protected pages with the hook/server function above so users are always redirected there until completion. On that page, validate details on your backend and then set the onboarded metadata flag. Follow the guide on Custom User Data for implementation details.

Here are examples of how to use the hook and server function in your components:

import { useOnboarding } from '@/app/onboarding-hooks';
import { useUser } from '@stackframe/stack';

export default function HomePage() {
  useOnboarding();
  const user = useUser();

  return <div>Welcome to the app, {user.displayName}</div>;
}