


















Welcome to the second part of building a custom user menu! In the first part, we built a replacement for Clerk’s <UserButton /> using the Radix Dropdown primitive and some of Clerk’s hooks. Now we’ll be upgrading our user button with sign-in functionality when the user is not logged in as well as improve the behavior of the component in several ways.
The first step is to refactor the component so it's ready to build upon. Take the contents of the return and create a new component in the file above the exported component. Call the new component <UserButtonAndMenu /> and paste the copied code. We will need to add in the destructures for the user method from the useUser() hook, signOut(), and openUserProfile() from the userClerk() hook, and the router method from the userRouter() hook.
The first step is to refactor the component so it's ready to build upon.
<UserButtonAndMenu /><UserButton /> into the <UserButtonAndMenu /> componentuser method from useUser() hook to the new componentsignOut(), and openUserProfile() from userClerk() hook to the new componentrouter method from userRouter() hook to the new component\In the <UserButton /> component, we’re going to leave the check for isLoaded and add a user.id check using if ( !user.id ) {} and return the <SignInButton /> component from Clerk if there is no user.
If you saved your work and tested it, you will see that the refactoring has already accomplished the base goal — it is now both a Sign-In button and a User Button/User Menu. We can still improve the component and provide a better user experience, so let’s do a few more refactors.
The first step is moving the button to its component. We’ll also add in a forwardRef to plan for the later improvements.
The second step is to refactor part of the <UserButtonAndMenu /> component. We want to take advantage of the new <Button />
The third step is to refactor the top-level <UserButton /> component to also use the new <Button /> component. This button will use openSignIn() from useClerk() to programmatically open the sign-in modal. This results in a custom button and removal of the Clerk <SignInButton />
You can see that we left the user profile image inside of <UserMenuAndButton /> and passed it to the <Button /> as a child. Depending on your need you could hoist the image handling into the <Button /> — that’s totally up to the needs of what you’re building.
Everything is working nicely at this point, and the structure is in a great place to build on. That said, we can add a few refinements to elevate the user experience. Let’s start by installing an icon package and the class-variance-authority package.
With that installed, let’s do another refactor of the Button. We’ll use class-variance-authority to expand on what the button can do. This is a very structured approach and provides TypeScript support. We will set up a variant and a size, use the resulting primary and regular for the main button, and menu and small for the buttons in the dropdown menu.
Now that our <Button /> has reached its final form, let’s import some icons.
Let’s modify the button that serves as the trigger for the User Menu. We’ll use Clerk’s hasImage value from the user return of useUser(). Using them will let us display the UserIcon we just imported when the user hasn’t set a profile image, but use their image when they have. We will also will move the logic we have for the label for the button up.
We can use the ArrowRightCircleIcon to add a little flare to the Sign-In button.
Lastly, we will use UserIcon, ArrowRightEndOnTectangleIcon, and CurrencyDollarIcon to add icons to the drop-down menu. At the same time we will add the variant and size to the buttons so they are using the new configuration.
We can add a few finishing touches to the component to flesh it out some more with a few smaller tweaks and improvements:
accent variant, and then use that for the user button.menu variant buttons unique styling.className="min-w-[192px]" to the sign-in and user button to help give them a more consistent width.ArrowPathIcon to the icon import create a button for !isLoaded, and give it a loading/spanning state.outline-none class to remove the focus ring from the menu itemsWith this component you have the building blocks to build out your own user button and menu. Add the new entries to the dropdown that you need for your application, and use the tools provided by Radix and cva to design and style your component so it matches your application's design language!
Take a look at our Custom Flows documentation to explore more ways to customize your application using the many hooks and methods Clerks provides. The ability to add the pieces you need from Clerk to fully custom and unique UI provides flexibility to projects.
For more in-depth technical inquiries or to engage with our community, feel free to join our Discord. Stay in the loop with the latest Clerk features, enhancements, and sneak peeks by following our X account, @clerk. Your journey to seamless user management starts here!
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。