Avatar
Centralized avatars, rendered by user id. `ElvixUserAvatar` shows ANY user’s photo from their id alone (no session, no membership) with a custom/OAuth/initials fallback and live cross-app updates. `ElvixAvatar` is the editable wizard that writes the user’s one global photo.
Same image, same custom / Google / initials fallback chain, same CDN. The only difference is whether the host wants the wizard mounted. Pick the editable one on profile and settings surfaces. Pick the read-only one everywhere else.
Editable: ElvixAvatar
In-place wizard: display, choice (replace or remove), crop via react-easy-crop, remove confirm, working spinner. Every pane stays inside the circle at the same size, so reserve that square in your layout. Mount inside an <ElvixProvider>.
"use client";import { ElvixAvatar, ElvixProvider } from "@elvix.is/sdk/react";export function ProfileAvatar({ applicationId }: { applicationId: string }) {return (<ElvixProvider clientId={applicationId}><ElvixAvatarapplicationId={applicationId}size={128}onChange={(next) => {// Re-key downstream <ElvixUserAvatar> renders with next.updatedAt// to bust the CDN cache without a full page reload.console.log("avatar updated", next.sizes, next.updatedAt);}}/></ElvixProvider>);}
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
applicationId | string | optional | provider app | App scope for the change event/context. Accepts EITHER your public clientId OR the internal Application.id. NOTE: the photo itself is CENTRALIZED (0.10.0+) — editing or removing it here changes the user's ONE elvix photo everywhere, not a per-app copy. Omit it to inherit the app from <ElvixProvider>. |
mode | "edit" | "view" | optional | "edit" | edit mounts the in-place wizard. view renders the read-only, live-updating display (delegates to ElvixUserAvatar) — one component for both surfaces. |
size | number | optional | 128 | Pixel diameter. Pinned across every pane (no expand-on-edit). |
onChange | (next: { sizes: number[]; updatedAt: string }) => void | optional | none | Fires on successful upload or remove. updatedAt is your CDN cache-buster. |
onResult | (r: ElvixActionResult) => void | optional | none | Terminal callback for every attempt (success + failure). Safe metadata only. |
Read-only: ElvixUserAvatar
Static avatar with retina via srcset. No edit affordance, no click target, no callbacks. Default 40px (the navigation-chip size); override size for comment lists, profile headers, or larger surfaces.
"use client";import { ElvixProvider, ElvixUserAvatar } from "@elvix.is/sdk/react";// Three sizes, three places. Same component, no wizard, no callbacks.export function NavUserChip({ name }: { name: string }) {return (<ElvixProvider clientId="your-app"><div className="flex items-center gap-2"><ElvixUserAvatar size={32} /><span>{name}</span></div></ElvixProvider>);}
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
size | number | optional | 40 | Pixel diameter (1x CSS px). srcset handles retina automatically. |
shape | "circle" | "square" | optional | "circle" | Round chip or square tile. |
appSlug | string | optional | provider | Overrides the app slug. Falls back to the <ElvixProvider> bootstrap. |
userId | string | optional | provider | Render ANY user by id. Fetches that user's CENTRALIZED avatar from /public/api/users/<id>/media-meta with NO session and NO membership prop — the photo they set once in their elvix account, visible everywhere. Omit to render the signed-in user from <ElvixProvider> context. |
name | string | optional | — | Display name for the initials fallback + aria-label. Pass this in by-id mode: the endpoint returns photo meta only, never identity text, so the host supplies the name it already holds. |
membership | { avatarUpdatedAt: Date | number; avatarSizes: number[] } | optional | provider | Pre-resolved membership envelope. When supplied, NO by-id fetch happens (the host already holds the meta). avatarUpdatedAt is the CDN cache-buster. |
user | { name?: string | null; email?: string | null; avatarUrl?: string | null } | optional | provider | Override the user envelope when you already have it in scope. |
fallbackSrc | string | optional | initials | Your own placeholder IMAGE for the no-photo state, shown instead of the initials chip. A URL on your origin or a data: URI. Ignored once a custom or OAuth photo resolves. |
className | string | optional | "" | Passes through to the rendered element. |
Any user, by id — centralized (0.10.0+)
A user's photo is CENTRALIZED in elvix: set once in any elvix-powered app (or the account dashboard), it appears everywhere. Pass userId + name and ElvixUserAvatar fetches that user's account photo from the public /public/api/users/<id>/media-meta endpoint — NO session, NO membership envelope, NO per-app lookup. This is how you render a third party you only know by elvix id: a skill owner, a comment author, a teammate in a list. The endpoint is origin-gated (your app's registered origins) and returns photo meta only — never name or email — so you pass the name you already hold for the initials fallback.
"use client";import { ElvixProvider, ElvixUserAvatar } from "@elvix.is/sdk/react";// Render a THIRD party — a skill owner, a comment author — knowing only// their elvix user id. No session, no membership prop, no per-app data.// The photo is whatever they set once in their elvix account.export function SkillOwner({ ownerId, ownerName }: { ownerId: string; ownerName: string }) {return (<ElvixProvider clientId="your-app"><ElvixUserAvatar userId={ownerId} name={ownerName} size={40} /></ElvixProvider>);}
Fetches are cached + de-duplicated per user id, so a grid of 50 rows for the same author fires ONE request. The image URL carries a ?v=<updatedAt> cache-buster, so a photo change surfaces to every consumer within the endpoint's short cache window.
Live updates, no wiring (0.9.4+). When ElvixAvatar uploads or removes an image, every ElvixUserAvatar (and ElvixAvatar mode="view") for the same user updates live — same tab, across tabs, AND across apps + devices. Same-origin tabs sync instantly via an internal client store + BroadcastChannel; cross-app / cross-device changes arrive over a shared Server-Sent Events stream (/api/media/stream) within a few seconds. No websocket, no manual re-key, no reload. onChange.updatedAt is still your CDN cache-buster for server-rendered surfaces that mount fresh.
The editable widget never resizes between panes. Inside a flex row the cropping pane will not push siblings around, but it captures pointer events inside the circle until the user confirms or backs out.
Related
Banner
Centralized banners, rendered by user id. `ElvixUserBanner` shows ANY user’s 3:1 hero from their id alone (no session). `ElvixBanner` is the editable in-frame wizard that writes the user’s one global banner.
ReadElvixIdentityForm
Hosted profile editor: name, locale, timezone, pronouns, contact. Plugs straight into the elvix User record. In-frame edit → confirm → done lifecycle.
Read