DEV Community

Saurabh Paryani
Saurabh Paryani

Posted on

Creating a User Nav bar in Next.js 14 using Shadcn UI and Tailwind CSS

The goal for this article is to build something like:

User Nav

When I was working on a project (https://github.com/saurabhparyani), I made a nav bar that looked something like:

Nav bar showing user image on the right

<div className="flex items-center gap-x-2 ms-auto md:col-span-3">
        {user ? (
          <UserNav/>
        ) : (
          <div className="flex items-center gap-x-2">
            <Button asChild>
              <LoginLink>Log in</LoginLink>
            </Button>
            <Button variant="secondary" asChild>
              <RegisterLink>Register</RegisterLink>
            </Button>
          </div>
        )}
</div>
Enter fullscreen mode Exit fullscreen mode

I used KindeAuth for authentication and when I signed up by Google, I rendered my user image on the right.

When clicked on the user avatar (in this case, "S" because my google avatar is just my name), it should give me all sort of options like:

  1. The user's name
  2. The user's email address
  3. Some properties you wish to add
  4. A Log out button

Let's begin building!

So without the user nav and the nav bar, my project structure looks like:

project structure

To make a User Nav, create a component inside the app folder and name it UserNav.tsx.

export function UserNav() {
    return (
        <div>User Nav content</div>
    )
}
Enter fullscreen mode Exit fullscreen mode

We want a functionality that when a user clicks on the avatar, the button trigger should open a dropdown menu from which the user can see all sort of options.

To do that, we will use Shadcn UI. Since we need to show the user name and email, we will use the Dropdown component. We also need the Avatar component to display the user image as a nice round avatar. And if you have an authentication feature in your project, you can include a Log Out button so for that, we also install the Button component from Shadcn.

https://ui.shadcn.com/docs/components/dropdown-menu
https://ui.shadcn.com/docs/components/avatar
https://ui.shadcn.com/docs/components/button

Install these packages and you're ready to code.

The User Avatar

Let's start with the avatar to display the user image.
To do that, add the DropdownMenu component from "@/components/ui/dropdown-menu" and NOT the radix-ui one or you would get a webpack error.

import correctly

Inside the DropdownMenu component, you first add a DropdownMenuTrigger component from the same @/components/ui folder. This component triggers the button click and shows the Avatar.

import { DropdownMenu, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";

export function UserNav() {
    return (
        <DropdownMenu>
            <DropdownMenuTrigger>
            </DropdownMenuTrigger>
        </DropdownMenu>
    )
}
Enter fullscreen mode Exit fullscreen mode

But for a trigger, we need a button to click so add the Button component installed inside it. The button component should have the Avatar component, which further contains the AvatarFallback component.

import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export function UserNav() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button>
          <Avatar>
            <AvatarFallback>S</AvatarFallback>
          </Avatar>
        </Button>
      </DropdownMenuTrigger>
    </DropdownMenu>
  );
}
Enter fullscreen mode Exit fullscreen mode

I've added an S in the AvatarFallback as an example of how my name's first letter would look as an avatar.

The above code when saved, shows something like:

ugly avatar

We need to style the button to see this clear, so after adding some styling to the Button and Avatar component....

import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export function UserNav() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>
        <Button variant="ghost" className="relative h-10 w-10 rounded-full">
          <Avatar className="h-10 w-10">
            <AvatarFallback>S</AvatarFallback>
          </Avatar>
        </Button>
      </DropdownMenuTrigger>
    </DropdownMenu>
  );
}
Enter fullscreen mode Exit fullscreen mode

...we get something looking like this:

better button
Later, we can add the user image to this button inside the Avatar component.

DropdownMenuContent

The DropdownMenuTrigger actives the drop down when clicked on the avatar button. The following code will be the content.

<DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="text-sm font-medium leading-none">Saurabh</p>
          </div>
        </DropdownMenuLabel>
</DropdownMenuContent>
Enter fullscreen mode Exit fullscreen mode

Inside the DropdownMenuContent, we define labels as our items with the DropdownMenuLabel component. Inside which, we will add our item names.
The above code, when clicked on the button, would look something like:

Dropdown menu label name

We will similarly add another item as a

tag with the email and style it.

<DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="text-sm font-medium leading-none">Saurabh</p>
            <p className="text-xs leading-none text-muted-foreground">
              saurabhparyani@64gmail.com
            </p>
          </div>
        </DropdownMenuLabel>
</DropdownMenuContent>
Enter fullscreen mode Exit fullscreen mode

dropdown menu label email

DropdownMenuSeparator

Add a small line as a separator after the name and email, to add more list items as per your need.

i

mport { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export function UserNav() {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>
        <Button variant="ghost" className="relative h-10 w-10 rounded-full">
          <Avatar className="h-10 w-10">
            <AvatarFallback>S</AvatarFallback>
          </Avatar>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="text-sm font-medium leading-none">Saurabh</p>
            <p className="text-xs leading-none text-muted-foreground">
              saurabhparyani@64gmail.com
            </p>
          </div>
        </DropdownMenuLabel>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
Enter fullscreen mode Exit fullscreen mode

DropDownMenu group and item

Log out

Just add another DropDownMenu item that says Log out. Since I'm using Kinde, I can just add a

import { LogoutLink } from "@kinde-oss/kinde-auth-nextjs/components";

and add a,

<DropdownMenuItem asChild>
          <LogoutLink>Log out</LogoutLink>
</DropdownMenuItem>
Enter fullscreen mode Exit fullscreen mode

after the DropdownMenuSeparator.

dropdown with logout

Render User name, email and picture on the avatar.

To do this, first create an interface in your UserNav.tsx

interface iAppProps {
    email: string;
    name: string;
    userImage: string | undefined;
}
Enter fullscreen mode Exit fullscreen mode

And add this interface in the function,
export function UserNav({ email, name, userImage }: iAppProps) {

This will give an error in the Navbar.tsx file so go back and add these props over there.

<div className="flex items-center gap-x-2 ms-auto md:col-span-3">
        {user ? (
          <UserNav
            email={user.email as string}
            name={user.given_name as string}
            userImage={
              user.picture ?? `https://avatar.vercel.sh/${user.given_name}`
            }
          />
        ) : (
          <div className="flex items-center gap-x-2">
            <Button asChild>
              <LoginLink>Log in</LoginLink>
            </Button>
            <Button variant="secondary" asChild>
              <RegisterLink>Register</RegisterLink>
            </Button>
          </div>
        )}
</div>
Enter fullscreen mode Exit fullscreen mode

The user object comes from Kinde,

const { getUser } = getKindeServerSession();
const user = await getUser();
Enter fullscreen mode Exit fullscreen mode

The user may not provide a picture if they are signing up with their email. So use https://avatar.vercel.sh/${user.given_name}
to generate a random picture based on any given name.

Once you add the props to the Nav bar, simply add the userImage inside the Avatar component to display the image.

The final UserNav.tsx would look something like:

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { LogoutLink } from "@kinde-oss/kinde-auth-nextjs/components";

interface iAppProps {
  email: string;
  name: string;
  userImage: string | undefined;
}

export function UserNav({ email, name, userImage }: iAppProps) {
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="ghost" className="relative h-10 w-10 rounded-full">
          <Avatar className="h-10 w-10">
            <AvatarImage src={userImage} alt="User Image" />
            <AvatarFallback>{name[0]}</AvatarFallback>
          </Avatar>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end" className="w-56" forceMount>
        <DropdownMenuLabel className="font-normal">
          <div className="flex flex-col space-y-1">
            <p className="text-sm font-medium leading-none">{name}</p>
            <p className="text-xs leading-none text-muted-foreground">
              {email}
            </p>
          </div>
        </DropdownMenuLabel>
        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
          <DropdownMenuItem>test</DropdownMenuItem>
        </DropdownMenuGroup>
        <DropdownMenuSeparator />
        <DropdownMenuItem asChild>
          <LogoutLink>Log out</LogoutLink>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
Enter fullscreen mode Exit fullscreen mode

final user nav

That concludes this article on how you can use Shadcn and Tailwind to style and create a customizable User Nav bar rendering your profile picture, name and email address.

Thanks for reading! <3

Top comments (0)