Docs

Add custom pages and links to the <UserProfile /> component

The <UserProfile /> component supports the addition of custom pages and use of external links in the navigation sidebar.

There are two ways to render the <UserProfile /> component:

  • As a modal
  • As a dedicated page

Both can be accessed when the user selects the <UserButton />, and then selects the Manage account option.

This guide includes examples for both use cases. You can select one of the following two tabs on the code examples to see the implementation for your preferred use case:

  • <UserButton /> tab: By default, the <UserButton /> sets userProfileMode='modal'. If you are using the default settings, then you should select this tab.
  • Dedicated page tab: If you do not want the <UserProfile /> to open as a modal, then you should select this tab. For these examples, you need to set userProfileMode='navigation' and userProfileUrl='/user-profile' on the <UserButton /> component.

Important

If your app is rendered with React Server Components by default, you'll need to add the use client directive when using <UserProfile />.

Add a custom page to <UserProfile />

Custom pages can be rendered inside the <UserProfile /> component and provide a way to incorporate app-specific settings or additional functionality.

To add a custom page to the <UserProfile /> component, use the <UserButton.UserProfilePage /> component or the <UserProfile.Page /> component, depending on your use case.

Props

<UserButton.UserProfilePage /> and <UserProfile.Page /> accept the following props, all of which are required:

  • Name
    label
    Type
    string
    Description

    The name that will be displayed in the navigation sidebar for the custom page.

  • Name
    labelIcon
    Type
    React.ReactElement
    Description

    An icon displayed next to the label in the navigation sidebar.

  • Name
    url
    Type
    string
    Description

    The path segment that will be used to navigate to the custom page. For example, if the <UserProfile /> component is rendered at /user, then the custom page will be accessed at /user/{url} when using path routing.

  • Name
    children
    Type
    React.ReactElement
    Description

    The components to be rendered as content inside the custom page.

Example

The following example demonstrates two ways that you can render content in the <UserButton.UserProfilePage /> or <UserProfile.Page /> component: as a component or as a direct child.

/app/page.tsx
'use client'

import { UserButton } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

const CustomPage = () => {
  return (
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  )
}

export default function Home() {
  return (
    <header>
      <UserButton>
        {/* You can pass the content as a component */}
        <UserButton.UserProfilePage label="Custom Page" url="custom" labelIcon={<DotIcon />}>
          <CustomPage />
        </UserButton.UserProfilePage>

        {/* You can also pass the content as direct children */}
        <UserButton.UserProfilePage label="Terms" labelIcon={<DotIcon />} url="terms">
          <div>
            <h1>Custom Terms Page</h1>
            <p>This is the custom terms page</p>
          </div>
        </UserButton.UserProfilePage>
      </UserButton>
    </header>
  )
}
/app/user-profile/[[...user-profile]]/page.tsx
'use client'

import { UserProfile } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

const CustomPage = () => {
  return (
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  )
}

const UserProfilePage = () => (
  <UserProfile path="/user-profile" routing="path">
    {/* You can pass the content as a component */}
    <UserProfile.Page label="Custom Page" labelIcon={<DotIcon />} url="custom-page">
      <CustomPage />
    </UserProfile.Page>

    {/* You can also pass the content as direct children */}
    <UserProfile.Page label="Terms" labelIcon={<DotIcon />} url="terms">
      <div>
        <h1>Custom Terms Page</h1>
        <p>This is the custom terms page</p>
      </div>
    </UserProfile.Page>
  </UserProfile>
)

export default UserProfilePage
pages/index.astro
---
import { UserButton } from '@clerk/astro/components'
---

<header>
  <UserButton>
    <UserButton.UserProfilePage label="Custom Page" url="custom">
      <svg
        slot="label-icon"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
        fill="currentColor"
      >
        <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
      </svg>
      <div>
        <h1>Custom Profile Page</h1>
        <p>This is the custom profile page</p>
      </div>
    </UserButton.UserProfilePage>
  </UserButton>
</header>
pages/user-profile.astro
---
import { UserProfile } from '@clerk/astro/components'
---

<UserProfile>
  <UserProfile.Page label="Custom Page" url="custom-page">
    <svg
      slot="label-icon"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      fill="currentColor"
    >
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
    </svg>
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  </UserProfile.Page>
</UserProfile>

JavaScript example

To add custom pages to the <UserProfile /> component using the JavaScript SDK, you can pass the customPages property to the mountUserProfile() or openUserProfile() method, as shown in the following example:

main.js
import { Clerk } from '@clerk/clerk-js'

const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY

const clerk = new Clerk(pubKey)
await clerk.load()

document.getElementById('app').innerHTML = `
  <div id="user-profile"></div>
`

const userProfileDiv = document.getElementById('user-profile')

clerk.openUserProfile(userProfileDiv, {
  customPages: [
    {
      url: 'custom-page',
      label: 'Custom Page',
      mountIcon: (el) => {
        el.innerHTML = '👋'
      },
      unmountIcon: (el) => {
        el.innerHTML = ''
      },
      mount: (el) => {
        el.innerHTML = `
          <h1><b>Custom Page</b></h1>
          <p>This is the content of the custom page.</p>
          `
      },
      unmount: (el) => {
        el.innerHTML = ''
      },
    },
    {
      url: '/other-page',
      label: 'Other Page',
      mountIcon: (el) => {
        el.innerHTML = '🌐'
      },
      unmountIcon: (el) => {
        el.innerHTML = ''
      },
    },
  ],
})

You can add external links to the <UserProfile /> navigation sidebar using the <UserButton.UserProfileLink /> component or the <UserProfile.Link /> component, depending on your use case.

Props

<UserButton.UserProfileLink /> and <UserProfile.Link /> accept the following props, all of which are required:

  • Name
    label
    Type
    string
    Description

    The name that will be displayed in the navigation sidebar for the link.

  • Name
    labelIcon
    Type
    React.ReactElement
    Description

    An icon displayed next to the label in the navigation sidebar.

  • Name
    url
    Type
    string
    Description

    The absolute or relative url to navigate to

Example

The following example adds a link to the homepage in the navigation sidebar of the <UserProfile /> component.

/app/page.tsx
'use client'

import { UserButton } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

export default function Home() {
  return (
    <header>
      <UserButton>
        <UserButton.UserProfileLink label="Homepage" url="/" labelIcon={<DotIcon />} />
      </UserButton>
    </header>
  )
}
/app/user-profile/[[...user-profile]]/page.tsx
'use client'

import { UserProfile } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

const UserProfilePage = () => (
  <UserProfile path="/user-profile" routing="path">
    <UserProfile.Link label="Homepage" labelIcon={<DotIcon />} url="/" />
  </UserProfile>
)

export default UserProfilePage
pages/index.astro
---
import { UserButton } from '@clerk/astro/components'
---

<header>
  <UserButton>
    <UserButton.UserProfileLink label="Homepage" url="/">
      <svg
        slot="label-icon"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
        fill="currentColor"
      >
        <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
      </svg>
    </UserButton.UserProfileLink>
  </UserButton>
</header>
pages/user-profile.astro
---
import { UserProfile } from '@clerk/astro/components'
---

<UserProfile>
  <UserProfile.Link label="Homepage" url="/">
    <svg
      slot="label-icon"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      fill="currentColor"
    >
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
    </svg>
  </UserProfile.Link>
</UserProfile>

Reorder default routes

If you want to reorder the default routes, Account and Security, set the label prop to 'account' or 'security'. This will target the existing default page and allow you to rearrange it.

Note that when reordering default routes, the first item in the navigation sidebar cannot be a <UserButton.UserProfileLink /> or <UserProfile.Link /> component.

/app/page.tsx
'use client'

import { UserButton } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

const CustomPage = () => {
  return (
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  )
}

export default function Home() {
  return (
    <header>
      <UserButton>
        <UserButton.UserProfilePage label="Custom Page" url="custom" labelIcon={<DotIcon />}>
          <CustomPage />
        </UserButton.UserProfilePage>
        <UserButton.UserProfileLink label="Homepage" url="/" labelIcon={<DotIcon />} />
        <UserButton.UserProfilePage label="account" />
        <UserButton.UserProfilePage label="security" />
      </UserButton>
    </header>
  )
}

The first page in the list will always be rendered under the root path defined with the path prop. Its url prop will be ignored.

In the following example, path is set to /user-profile, so the <CustomPage /> is rendered under the /user-profile path.

/app/user-profile/[[...user-profile]]/page.tsx
'use client'

import { UserProfile } from '@clerk/nextjs'

const DotIcon = () => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z" />
    </svg>
  )
}

const CustomPage = () => {
  return (
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  )
}

const UserProfilePage = () => (
  <UserProfile path="/user-profile" routing="path">
    <UserProfile.Page label="Custom Page" url="custom" labelIcon={<DotIcon />}>
      <CustomPage />
    </UserProfile.Page>
    <UserProfile.Link label="Homepage" url="/" labelIcon={<DotIcon />} />
    <UserProfile.Page label="account" />
    <UserProfile.Page label="security" />
  </UserProfile>
)

export default UserProfilePage
pages/index.astro
---
import { UserButton } from '@clerk/astro/components'
---

<header>
  <UserButton>
    <UserButton.UserProfilePage label="Custom Page" url="custom">
      <svg
        slot="label-icon"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
        fill="currentColor"
      >
        <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
      </svg>
      <div>
        <h1>Custom Profile Page</h1>
        <p>This is the custom profile page</p>
      </div>
    </UserButton.UserProfilePage>
    <UserButton.UserProfileLink label="Homepage" url="/">
      <svg
        slot="label-icon"
        xmlns="http://www.w3.org/2000/svg"
        viewBox="0 0 512 512"
        fill="currentColor"
      >
        <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
      </svg>
    </UserButton.UserProfileLink>
    <UserButton.UserProfilePage label="account" />
    <UserButton.UserProfilePage label="security" />
  </UserButton>
</header>

The first page in the list will always be rendered under the root path defined with the path prop. Its url prop will be ignored.

In the following example, path is set to /user-profile, so the custom page is rendered under the /user-profile path.

pages/user-profile.astro
---
import { UserProfile } from '@clerk/astro/components'
---

<UserProfile path="/user-profile" routing="path">
  <UserProfile.Page label="Custom Page" url="custom">
    <svg
      slot="label-icon"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      fill="currentColor"
    >
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
    </svg>
    <div>
      <h1>Custom Profile Page</h1>
      <p>This is the custom profile page</p>
    </div>
  </UserProfile.Page>
  <UserProfile.Link label="Homepage" url="/">
    <svg
      slot="label-icon"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      fill="currentColor"
    >
      <path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z"></path>
    </svg>
  </UserProfile.Link>
  <UserProfile.Page label="account" />
  <UserProfile.Page label="security" />
</UserProfile>

With the above example, the <UserProfile /> navigation sidebar will be in the following order:

  1. Custom Page
  2. Homepage
  3. Account
  4. Security

Feedback

What did you think of this content?

Last updated on