Account Menu
The navbar account control — sign-in glyph, avatar once signed in, and a menu with identity, links and sign out.
The navbar account control: a sign-in glyph when signed out, the reader's avatar when signed in, and a menu carrying their identity, your links and a sign-out row.
Upstreamed from YunNEWS, which handles the signed-in swap better than the other apps. Purely presentational — you fetch the session, supply every label and own each action.
Import
1import { AccountMenu } from "@yuhuanowo/yunui/ai";The three states
user is a three-state prop, and that is the whole point:
user | Renders |
|---|---|
undefined | a fixed-size placeholder — the answer isn't known yet |
null | the sign-in glyph |
| an object | the avatar (or a fallback glyph) plus the menu |
A session cookie is usually httpOnly, so the page cannot read it and only the server can answer. While the answer is pending, guessing "signed out" and then swapping to an avatar makes every page load look like it signed the reader out — so this holds the space instead.
Signed in
Click the avatar to open the menu.
Signed in
Wiring it up
1const [user, setUser] = useState<AccountMenuUser | null | undefined>(undefined);2 3useEffect(() => {4 let alive = true;5 api.me().then((d) => alive && setUser(d?.user ?? null));6 return () => { alive = false; };7}, []);8 9<AccountMenu10 user={user}11 signInHref="/login"12 items={[{ key: "keys", label: t("apiKeys"), icon: KeyRound, href: "/account/keys" }]}13 onSignOut={async () => { await api.logout(); setUser(null); router.refresh(); }}14 labels={{ signIn: t("signIn"), signOut: t("signOut") }}15/>Omit onSignOut to hide the sign-out row. An item with onSelect renders as a button instead of a link.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | |
| items | AccountMenuItem[] | [] | Menu entries above the sign-out row. |
| labels | { signIn?: string; signOut?: string; menu?: string | undefined; fallbackName?: string | undefined; } | undefined | — | Every string this renders — localize them yourself. |
| onSignOut | (() => void) | — | Called when the sign-out row is chosen. Omit to hide the row. |
| signInHref* | string | — | Where the signed-out button links. |
| user* | AccountMenuUser | null | undefined | — | The signed-in user, `null` when signed out, or `undefined` while the answer is still unknown. The three-state shape is the point: a session cookie is usually `httpOnly`, so the page cannot read it and only the server can answer. While it is `undefined` this renders a fixed-size placeholder rather than guessing — showing "sign in" first and swapping to an avatar makes every page load look like it signed the reader out. |