Display the User in the Header

In the previous section, we added a sign-in button to the header that triggers the GitHub OAuth flow. Once the user signs in, though, the header does not change at all. It says “Sign out” and gives no indication of who is signed in. In this section, we will fix that by subscribing to the currentUser query we wrote in the previous section and rendering the user’s name and avatar next to the sign-out button.

Add SignedOutButton Component

Create a src/components/sign-out-button.tsx component that renders the signed-in user’s name and avatar, along with a Sign out button:

import { useQuery } from "convex/react";
import { LogOut } from "lucide-react";
import { Button } from "@/components/ui/button";
import { api } from "../../convex/_generated/api";

function SignedOutButton({ onSignOut }: { onSignOut: () => void }) {
  const user = useQuery(api.users.currentUser);

  return (
    <div className="flex items-center gap-3">
      {user && (
        <div className="flex items-center gap-2">
          {user.image && (
            <img
              src={user.image}
              alt={user.name ?? "User avatar"}
              className="h-8 w-8 rounded-full border"
            />
          )}
          {user.name && (
            <span className="text-sm font-medium">{user.name}</span>
          )}
        </div>
      )}
      <Button variant="outline" onClick={onSignOut}>
        <LogOut className="mr-2 h-4 w-4" />
        Sign out
      </Button>
    </div>
  );
}

export default SignedOutButton;

The user can be briefly undefined. That is useQuery’s loading state. The {user && (...)} guard handles it by rendering only the sign-out button until the user document arrives. Users see the Sign out button first, and then their name and avatar appear a moment later. That gap is short, so most people do not notice it.

The user.image field comes from GitHub. When Convex Auth finishes the OAuth flow, it pulls your GitHub profile (public name + avatar URL + email) and stores those fields in the users row it creates. The image field holds the URL of your GitHub profile picture, served from avatars.githubusercontent.com.

Render SignedOutButton in the Header

Now update src/components/sign-in-button.tsx to render SignedOutButton when the user is authenticated:

  1. Import SignedOutButton:

    import SignedOutButton from "./sign-out-button";
    
  2. Replace the <Authenticated> branch with:

    <Authenticated>
      <SignedOutButton onSignOut={() => void signOut()} />
    </Authenticated>
    

Notice that with the current setup, the useQuery(api.users.currentUser) call, which lives in SignedOutButton, only runs when the user is authenticated. That is because SignedOutButton is only rendered inside the <Authenticated> component. If we had put the query directly in SignInButton, it would run even when <Unauthenticated> is the visible branch, which is wasteful (it would always return null, but Convex would still subscribe). Scoping it inside the component that <Authenticated> renders means the query only runs while a user is signed in.

I admit that placing a SignedOutButton component inside a SignInButton component is a little weird. We could rename it to UserMenu or something more generic, but for now, let’s leave it as it is.

Try It Out

Save the file and reload the app. If you are already signed in, you will see your GitHub name and avatar appear in the header to the left of the Sign out button.

User Information

If you are signed out, nothing changes on that side of the header because we did not touch the unauthenticated branch.

Sign out and back in, and watch the header. When you sign out, <Authenticated> stops rendering and the sign-in button comes back. When you sign in, your name and avatar show up again.

Checkpoint: Commit your progress.

git add .
git commit -m "tracker-05: Display the signed-in user's name and avatar in the header"
git push