Skip to content

NbUserMenu is the account menu every Nubisco product hangs off its shell: who you are signed in as, which other identities this browser holds, and the way out. It renders an avatar trigger and a panel teleported to <body>, so a sidebar with overflow: hidden cannot clip it.

vue
<template>
  <NbUserMenu
    :user="{ email: 'jose@nubisco.io', name: 'José Silva' }"
    @profile="router.push('/profile')"
    @sign-out="auth.signOut()"
  />
</template>

Picture ​

Give user.picture an image URL, such as the platform's OIDC picture claim, and the trigger shows it instead of initials:

vue
<NbUserMenu :user="{ email: me.email, name: me.name, picture: me.picture }" />

It is optional and it is allowed to fail. Without a picture, or when the image does not load, the trigger shows initials exactly as it did before. That second case is ordinary rather than exceptional: the platform gives a replaced avatar a new URL and answers the old one with a 404, so a product holding a stale URL would otherwise show a broken image. A new URL gets a fresh attempt.

In a rail that expands ​

A bare avatar belongs in a collapsed rail, where every item is an icon. In an expanded rail, where every item above it is a labelled row, it floats out of line and does not say whose account it is. trigger="identity" follows the rail instead:

vue
<template #sidebar-bottom>
  <NbUserMenu :user="user" trigger="identity" @sign-out="signOut" />
</template>

In an expanded rail it renders a row with the avatar, the name and the email, aligned to the menu items above it: the avatar's centre sits on the same line as their icons. In a collapsed rail it renders the avatar alone. It reads the rail's state from NbShell, so it changes when the rail does, with nothing to wire up. Outside a shell it shows the row. The row's accessible name is the visible name; nothing overrides it.

The default is trigger="avatar", which is unchanged: a product that does not opt in renders exactly as before. Pair identity with NbSidebarCollapseToggle and useSidebarVariant, as the app frame describes.

Accounts ​

Pass accounts when the product can enumerate the identities signed in on this browser: each one becomes a row, the current one is checked and inert, and the rest emit switch. Set accountsUnknown when that lookup failed, and the menu offers a generic Switch account action instead, so the identity provider can show its own chooser. Single-account products turn the whole section off with :show-account-actions="false".

Product entries ​

The default slot sits above the Profile / Sign out group, behind its own divider. It receives close, so a product entry can dismiss the menu as it navigates.

vue
<template>
  <NbUserMenu :user="user" v-slot="{ close }">
    <button
      class="my-entry"
      @click="
        close()
        go('/billing')
      "
    >
      Billing
    </button>
  </NbUserMenu>
</template>

The Nubisco Platform lockup ​

The foot of the panel signs the product as part of Nubisco Platform: the Nubisco mark at 13px and the line Powered by Nubisco Platform. It is a signature, not a control. It is not a link, takes no tab stop, carries no ARIA role, and has no hover state, because right now there is nothing to link to.

brand is on by default, so upgrading gets you the lockup without changing anything.

White-labelling: this is an opt-out, not an opt-in

The default renders the vendor's brand inside your product. If the app is sold under a client's name, or is deployed to a customer's own domain, set brand="none" (or fill the #brand slot with the client's line) at the single place you mount NbUserMenu, and do it before the first release rather than after someone spots a foreign logo in their account menu.

vue
<!-- White-label: no vendor signature anywhere in the menu. -->
<NbUserMenu :user="user" brand="none" />

We know the default is the wrong way round: nobody should have to opt out of another company's branding. It stays 'footer' because flipping it would silently strip the footer from every Nubisco product already shipping one, which is a breaking change, so it is queued for the next major. The prop and the slot are stable either way, an app that sets brand="none" today keeps working after the flip.

vue
<template>
  <!-- Default: the lockup, after the last divider. -->
  <NbUserMenu :user="user" brand="footer" />

  <!-- White-label deployments, and hosts that are not Nubisco products. -->
  <NbUserMenu :user="user" brand="none" />
</template>

The mark is NbNubiscoPlatformMark, the Nubisco Platform product mark rather than the Nubisco corporate one, since the lockup names the platform. It is the same component every other Nubisco surface renders, inlined rather than fetched, so the logo cannot drift from the one on the platform console and cannot break when the network does.

The panel is teleported to <body>, so it follows the site theme rather than any local preview wrapper: use the theme switch in the header to check the lockup on both grounds. The mark is one asset in both themes, and the text is token-only. Against --nb-c-layer-3, "Nubisco Platform" measures 13.94:1 light and 8.98:1 dark, and the "Powered by" prefix 10.51:1 and 7.01:1: comfortably past the 4.5:1 and 3:1 the lockup is held to.

brand is a string union rather than a boolean because a third mode, 'hub', is coming: the same lockup, but a real link to the Nubisco Platform product page, once that page exists.

Replacing the lockup ​

Products that must localise the lockup themselves, or restyle it to their own signature, use the #brand slot. It replaces the default lockup and keeps the divider above it.

vue
<template>
  <NbUserMenu :user="user">
    <template #brand>
      <p class="acme-brand">An Acme company</p>
    </template>
  </NbUserMenu>
</template>

Translations ​

Strings resolve in three steps: the host's global catalog under userMenu.* for the active locale, then the built-in default for the active language, then built-in English. English and Portuguese ship with the component; any other locale works by adding the keys to the host catalog, nothing in the library has to change.

ts
createI18n({
  legacy: false,
  locale: 'de',
  messages: {
    de: {
      userMenu: {
        SIGNED_IN_AS: 'Angemeldet als',
        SIGN_OUT: 'Abmelden',
        poweredBy: 'Bereitgestellt von Nubisco Platform',
      },
    },
  },
})

"Nubisco Platform" is a product name and stays untranslated inside the localised sentence, which is how the component knows which half of the line to emphasise. A translation that drops the name renders wholly emphasised, the higher-contrast half of the pair.

Placement ​

right-end (the default) opens the panel to the right of the trigger, aligned to its bottom, for a rail-style sidebar. top-start opens it above and left-aligned, for a trigger sitting at the bottom of a wide sidebar or in a header.

Props ​

PropTypeDefaultDescription
userIUserMenuUser(required)The identity the product is signed in as. { email, name?, picture? }. See Picture.
accountsIUserMenuAccount[]—Identities signed in on this browser, rendered as an inline switch list.
accountsUnknownbooleanfalseThe identities could not be determined. Renders a generic "Switch account" action instead of the list.
showAccountActionsbooleantrueHides the whole switch / add section for single-account products.
showProfilebooleantrueHides the Profile entry when the product has no profile page.
brand'footer' | 'none''footer''footer' renders the non-interactive Nubisco Platform lockup; 'none' renders nothing.
placement'right-end' | 'top-start''right-end'Where the panel opens relative to the trigger.
trigger'avatar' | 'identity''avatar''identity' follows the rail: a labelled row when expanded, the avatar alone when collapsed.
disabledbooleanfalseRenders the trigger inert.

Events ​

EventPayloadDescription
switchaccountAn inline account row was chosen. The menu closes.
removeaccountThe x on an account row was clicked. The menu stays open.
switch-account—"Switch account" was chosen (accounts unknown).
add-account—"Use another account" was chosen.
profile—The Profile entry was chosen.
sign-out—Sign out was chosen.
open / close—The panel opened or was dismissed.

Slots ​

SlotPropsDescription
defaultcloseProduct entries, in their own section above Profile / Sign out.
triggeropen, toggleReplaces the avatar button.
brand—Replaces the Nubisco Platform lockup. Not rendered at all when brand="none".

Exposed methods ​

MemberTypeDescription
openRef<boolean>Current visibility.
toggle() => voidOpens or closes the panel.
close() => voidCloses the panel.

Tokens used ​

TokenApplied to
--nb-c-layer-3, --nb-c-layer-border-3Panel surface, border and dividers
--nb-c-layer-hover-3Row hover
--nb-c-textEmail, account rows, "Nubisco Platform"
--nb-c-text-mutedThe "Powered by" prefix
--nb-c-text-subtleSection label, account name, remove affordance
--nb-c-primaryAvatar fill
--nb-c-success / --nb-c-dangerCurrent-account check / sign out
--nb-zindex-menuPanel stacking