Components
ElvixBanner

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.

ElvixBannereditable wizard
ElvixUserBannerread-only display

Same srcset hero, same gradient empty-state, same CDN. The only difference is whether the host wants the wizard mounted. Pick the editable one on the profile editor. Pick the read-only one on public profile hero, account dashboard, anywhere display-only.

Editable: ElvixBanner

In-frame replace + crop + remove wizard. The frame keeps its 3:1 aspect across every pane so it never pushes siblings around mid-edit. Mount inside an <ElvixProvider>.

app/profile/banner.tsx
"use client";
import { ElvixBanner, ElvixProvider } from "@elvix.is/sdk/react";
export function ProfileBannerEditor({ applicationId }: { applicationId: string }) {
return (
<ElvixProvider clientId={applicationId}>
<ElvixBanner
applicationId={applicationId}
cornerRadius={14}
onChange={(next) => {
// Re-key downstream <ElvixUserBanner> renders with next.updatedAt
// to bust the CDN cache without a full page reload.
console.log("banner updated", next.sizes, next.updatedAt);
}}
/>
</ElvixProvider>
);
}
PropTypeRequiredDefaultDescription
applicationIdstringoptionalprovider appApp scope. Accepts EITHER your public clientId OR the internal Application.id (both resolve server-side). Omit it to inherit the app from <ElvixProvider>.
cornerRadiusnumberoptional14Border-radius in px. Matches the SDK card chrome by default.
onChange(next: { sizes: number[]; updatedAt: Date | number }) => voidoptionalnoneFires on successful upload or remove. updatedAt is your CDN cache-buster.
onResult(r: ElvixBannerResult) => voidoptionalnoneTerminal callback for every attempt (success and failure). Safe metadata only, never image bytes.

Read-only: ElvixUserBanner

Static 3:1 hero with srcset. Fills its parent width by default; pass containerPx to tell the browser which variant to pick. No edit affordance, no callbacks.

components/public-profile-hero.tsx
"use client";
import { ElvixProvider, ElvixUserBanner } from "@elvix.is/sdk/react";
// Public profile hero. Pass userId only when rendering someone else.
export function PublicProfileHero({ userId }: { userId?: string }) {
return (
<ElvixProvider clientId="your-app">
<ElvixUserBanner userId={userId} containerPx={960} />
</ElvixProvider>
);
}
PropTypeRequiredDefaultDescription
userIdstringoptionalproviderRender ANY user by id. Fetches that user's CENTRALIZED banner from /public/api/users/<id>/media-meta with NO session and NO membership prop. Omit to render the signed-in user from <ElvixProvider> context.
appSlugstringoptionalproviderOverride the CDN app slug. Rarely needed — by-id mode reads it from the media-meta response; current-user mode from the bootstrap.
membership{ bannerUpdatedAt: Date | number; bannerSizes: number[] }optionalproviderOverride the membership envelope. bannerUpdatedAt is the CDN cache-buster, bannerSizes drives the srcset.
containerPxnumberoptionalparent widthContainer max-width in CSS px. Drives the sizes hint on srcset so the browser picks the smallest crisp variant.
classNamestringoptionalnonePasses through to the root element.
emptyClassNamestringoptionalgradientBackground class for the empty-state placeholder when no banner has been uploaded.
fallbackSrcstringoptionalgradientYour own placeholder IMAGE for the no-banner state, shown instead of the default gradient. A URL on your origin or a data: URI. Ignored once the user sets a real banner.

Any user, by id — centralized (0.10.0+)

Like ElvixUserAvatar, passing userId renders a THIRD party's banner from their elvix id alone — NO session, NO membership envelope. The banner is whatever that user set once in their elvix account; it appears the same on every elvix-powered app. Origin-gated to your app's registered origins, cached + de-duplicated per user id.

Live updates, no wiring (0.9.4+). When ElvixBanner uploads or removes, every ElvixUserBanner for the same user refreshes live — same tab, across tabs, and across apps + devices. Same-origin tabs sync instantly via BroadcastChannel; cross-app / cross-device changes arrive over the shared /api/media/stream SSE within a few seconds. No websocket, no manual re-key, no reload. onChange.updatedAt remains the CDN cache-buster for server-rendered surfaces that mount fresh. ElvixUserBanner also takes cornerRadius so its rounding matches ElvixBanner (both default to 14px).

The editable frame captures pointer events across every pane until the user confirms or backs out. It will not push siblings around, but adjacent click targets are blocked inside the frame area.

Related