Profile Menu
blocklayoutHeader-grade profile/account dropdown. Child-snippet DropdownMenu the consumer wraps around their own avatar button (default trigger ships an Avatar fallback). Sections: upgrade, account/billing/notifications/settings, log out. Calls `onselect` with the chosen item key so consumers wire navigation and auth themselves.
Installation
$pnpm dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/profile-menu.json$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/profile-menu.json$yarn dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/profile-menu.json$bunx shadcn-svelte@latest add https://uipkge.dev/r/svelte/profile-menu.jsonManifest:
https://uipkge.dev/r/svelte/profile-menu.jsonInstalls to:src/lib/components/blocks/profile-menu/Variants
Loading interactive previews…
Loading interactive previews…
npm dependencies
Includes
Files installed (1)
src/lib/components/blocks/profile-menu/ProfileMenu.svelte4.3 kB
<script lang="ts" module> import type { Snippet } from 'svelte' export interface ProfileMenuUser { name: string email: string avatar?: string } export type ProfileMenuKey = 'upgrade' | 'account' | 'billing' | 'notifications' | 'settings' | 'logout' export interface ProfileMenuProps { user?: ProfileMenuUser /** Custom trigger. Receives the user, the computed initials, and the * trigger props to spread onto your own element (Svelte has no automatic * as-child merge, so spreading `props` is what wires the toggle). */ children?: Snippet<[{ user: ProfileMenuUser; initials: string; props: Record<string, unknown> }]> onselect?: (key: ProfileMenuKey) => void } </script> <script lang="ts"> import { BadgeCheck, Bell, CreditCard, LogOut, Settings, Zap } from '@lucide/svelte' import { Avatar, AvatarFallback, AvatarImage } from '$lib/components/ui/avatar' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '$lib/components/ui/dropdown-menu' let { user = { name: 'Alex Morgan', email: 'alex@example.com', avatar: '' }, children, onselect, }: ProfileMenuProps = $props() const initials = $derived.by(() => { const parts = user.name.trim().split(/\s+/).slice(0, 2) return parts.map((p) => p[0]?.toUpperCase()).join('') || 'U' }) </script> <!-- DropdownMenu renders no DOM of its own, so the slot marker lives on a display:contents wrapper that vanishes from layout. --> <div data-slot="profile-menu" class="contents"> <DropdownMenu> {#if children} <DropdownMenuTrigger> {#snippet child({ props })} {@render children({ user, initials, props })} {/snippet} </DropdownMenuTrigger> {:else} <DropdownMenuTrigger class="hover:bg-accent focus-visible:ring-ring flex size-8 items-center justify-center rounded-lg transition-colors focus-visible:ring-1 focus-visible:outline-none" aria-label="Open profile menu" > <Avatar class="size-7"> {#if user.avatar} <AvatarImage src={user.avatar} alt={user.name} /> {/if} <AvatarFallback class="bg-primary/10 text-primary text-xs font-semibold"> {initials} </AvatarFallback> </Avatar> </DropdownMenuTrigger> {/if} <DropdownMenuContent class="w-60 rounded-lg" align="end" sideOffset={8}> <DropdownMenuLabel class="p-0 font-normal"> <div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar class="size-9 rounded-lg"> {#if user.avatar} <AvatarImage src={user.avatar} alt={user.name} /> {/if} <AvatarFallback class="bg-primary/10 text-primary rounded-lg text-xs font-semibold"> {initials} </AvatarFallback> </Avatar> <div class="grid flex-1 text-left leading-tight"> <span class="truncate text-sm font-semibold">{user.name}</span> <span class="text-muted-foreground truncate text-xs">{user.email}</span> </div> </div> </DropdownMenuLabel> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem onSelect={() => onselect?.('upgrade')}> <Zap class="size-4" /> Upgrade to Pro </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem onSelect={() => onselect?.('account')}> <BadgeCheck class="size-4" /> Account </DropdownMenuItem> <DropdownMenuItem onSelect={() => onselect?.('billing')}> <CreditCard class="size-4" /> Billing </DropdownMenuItem> <DropdownMenuItem onSelect={() => onselect?.('notifications')}> <Bell class="size-4" /> Notifications </DropdownMenuItem> <DropdownMenuItem onSelect={() => onselect?.('settings')}> <Settings class="size-4" /> Settings </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuItem onSelect={() => onselect?.('logout')}> <LogOut class="size-4" /> Log out </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </div>
Raw manifest:https://uipkge.dev/r/svelte/profile-menu.json