import { Persona } from "@elabs-ai/components-ai";npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-aiPaste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui Persona component (from @elabs-ai/components-ai) to this project. 1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`. 2. Look Persona up before writing code: `npx -y @elabs-ai/components-cli docs Persona` prints its real props, variants, what it pairs with and what to avoid. 3. If @elabs-ai/components-ai is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-ai` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui @elabs-ai/components-ai`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`. 4. Use it here: [the screen or file, and what it should show] 5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop. 6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.
Avoid
- Statically importing the Rive runtime — it is behind a dynamic import so the WebGL2 runtime and its .wasm stay out of every consumer's entry chunk (ADR 0019).
- Using it as the only in-progress signal — pair it with a real streaming affordance (Shimmer) that survives reduced motion.
- Dropping it in as decoration on every surface — it is the agent's identity, not a spinner.
Persona — the animated agent presence. The artwork is a Rive .riv file fetched from a remote origin at runtime, and the WebGL2 runtime is loaded lazily. Both facts matter for deployment: under a restrictive connect-src the fetch is blocked and the component falls back to a token-driven orb rather than an empty box.
Works with
Used inside
Pairs with
Examples
Custom Fallback
An explicit custom fallback, for brands that want their own resting mark.
States
Every conversational state, offline. With live artwork these animate; here they all show the fallback, which is exactly what a locked-down deployment sees. The caption under each orb is the SAME text `Persona` announces to assistive tech via its default `statusLabel` — shown visibly here purely for documentation.
No Status Announcement
Opt out of the announcement with `statusLabel={null}` — for a surface that already renders its own `role="status"` line elsewhere (e.g. next to a chat composer) and doesn't want AT told twice.
Live Remote Artwork
Live artwork from the default remote origin. ⚠️ This story REACHES THE NETWORK (`*.public.blob.vercel-storage.com`). It is the only one that does; it exists so the real animation can be reviewed, and it degrades to the fallback orb when the origin is unreachable.
API
Persona
| Prop | Type | Description |
|---|---|---|
className | string | |
fallback | ReactNode | Rendered while the artwork loads, and permanently if it fails (blocked by a `connect-src` CSP, offline, or a bad `src`). Defaults to a token-driven orb, so a blocked fetch never leaves an empty box. |
onLoad | PersonaRiveEventCallback | |
onLoadError | PersonaRiveEventCallback | |
onPause | PersonaRiveEventCallback | |
onPlay | PersonaRiveEventCallback | |
onReady | () => void | |
onStop | PersonaRiveEventCallback | |
src | string | Override the `.riv` artwork URL — point at a self-hosted copy when a CSP blocks the default origin. Defaults to `PERSONA_SOURCES[variant].source`. Mirrors the `mapStyle` override on `MapCanvas`. |
staterequired | PersonaState | |
statusLabel | ReactNode | null | Visually-hidden text announced when `state` changes. Defaults to a localized label for the current `state`; pass `null` when the consuming surface already renders its own `role="status"` line, so AT is not told twice. |
variant | PersonaVariant |