Reading and writing user information, and protecting pages

You can build custom components that access the current user in your app. This guide covers the functions and hooks that let you do this.

Client Component basics

The useUser() hook returns the current user in a Client Component. By default, it will return null if the user is not signed in.

'use client';
import { useUser } from '@stackframe/stack';

export function MyClientComponent() {
  const user = useUser();
  return <div>{user ? `Hello, ${user.displayName ?? 'anon'}` : 'You are not logged in'}</div>;
}

You can also use useUser({ or: "redirect" }) to automatically redirect to the sign-in page if the user is not signed in.

Server Component basics

Since useUser() is a stateful hook, you can't use it on server components. Instead, import stackServerApp and call getUser():

import { stackServerApp } from '@/stack';

export default async function MyServerComponent() {
  const user = await stackServerApp.getUser();
  return <div>{user ? `Hello, ${user.displayName ?? 'anon'}` : 'You are not logged in'}</div>;
}

Protecting a page

You can protect a page in three ways:

  • In Client Components with useUser({ or: "redirect" })
  • In Server Components with await getUser({ or: "redirect" })
  • With middleware

Client Component:

'use client';
import { useUser } from '@stackframe/stack';

export default function MyProtectedClientComponent() {
  useUser({ or: 'redirect' });
  return <h1>You can only see this if you are logged in</h1>;
}

Server Component:

import { stackServerApp } from '@/stack';

export default async function MyProtectedServerComponent() {
  await stackServerApp.getUser({ or: 'redirect' });
  return <h1>You can only see this if you are logged in</h1>;
}

Middleware:

export async function middleware(request) {
  const user = await stackServerApp.getUser();
  if (!user) {
    return Response.redirect(new URL('/handler/sign-in', request.url));
  }
  return Response.next();
}

User data

You can update attributes on a user object with the user.update() function (if your white-labeled setup allows it):

'use client';
import { useUser } from '@stackframe/stack';

export default function MyClientComponent() {
  const user = useUser();
  return (
    <button onClick={async () => await user.update({ displayName: 'New Name' })}>
      Change Name
    </button>
  );
}

You can also store custom user data in the clientMetadata, serverMetadata, or clientReadonlyMetadata fields.

Signing out

You can sign out the user by redirecting them to /handler/sign-out or by calling user.signOut():

'use client';
import { useUser } from '@stackframe/stack';

export default function SignOutButton() {
  const user = useUser();
  return user ? <button onClick={() => user.signOut()}>Sign Out</button> : 'Not signed in';
}

Example: Custom profile page

Stack automatically creates a user profile on sign-up. Here's an example page that displays this information:

'use client';
import { useUser, useStackApp, UserButton } from '@stackframe/stack';

export default function PageClient() {
  const user = useUser();
  const app = useStackApp();
  return (
    <div>
      {user ? (
        <div>
          <UserButton />
          <p>Welcome, {user.displayName ?? 'unnamed user'}</p>
          <p>Your e-mail: {user.primaryEmail}</p>
          <button onClick={() => user.signOut()}>Sign Out</button>
        </div>
      ) : (
        <div>
          <p>You are not logged in</p>
          <button onClick={() => app.redirectToSignIn()}>Sign in</button>
          <button onClick={() => app.redirectToSignUp()}>Sign up</button>
        </div>
      )}
    </div>
  );
}