Dialog Avatar Tutorial
Show a small portrait next to ADV dialog text with Avatar. Two steps:
- Tell the
Characterwhich image to use, or bind a stageImagefor expression-based portraits. - Render
<Avatar />inside your customDialog.
Resolution order, resolvers and types are on the Dialog reference and useAvatar.
1. Set a character-level avatar
One image for that character whenever they speak, including off-screen lines with no sprite on stage.
import { Character } from "narraleaf-react";
const alice = new Character("Alice", {
avatar: "/characters/alice/avatar.png",
});
alice.say("Players will see my portrait next to the text.");Chain style:
const bob = new Character("Bob").setAvatar("/characters/bob/avatar.png");An avatar requires a named character. Narrator lines (new Character(null)) never show one.
2. Add Avatar to your dialog UI
Avatar resolves the image for the current sentence and renders an <img>. With no avatar it renders nothing, so the layout reserves no empty hole.
Put it next to Nametag and Texts, the same layout the built-in dialog uses:
import {
Avatar,
Dialog,
Nametag,
Texts,
} from "narraleaf-react";
export function GameDialog() {
return (
<Dialog className="bg-black/75 rounded-xl px-5 py-4 text-white">
<div
className="flex items-start gap-4"
style={{ width: "100%", minHeight: 96 }}
>
<Avatar
className="shrink-0 rounded-lg border border-white/20"
style={{ width: 88, height: 88 }}
/>
<div className="min-w-0 flex-1 flex flex-col gap-1">
<Nametag className="text-base font-semibold" />
<Texts className="text-sm leading-relaxed" />
</div>
</div>
</Dialog>
);
}The default size without style is 96×96. Override it with className or style.
Optional: responsive layout with useAvatar
To let the text column grow when there is no avatar, read visibility from useAvatar:
import {
Avatar,
Dialog,
Nametag,
Texts,
useAvatar,
} from "narraleaf-react";
export function GameDialog() {
const { visible } = useAvatar();
return (
<Dialog className="bg-black/75 rounded-xl px-5 py-4">
<div
className="grid gap-4 items-start text-white"
style={{
gridTemplateColumns: visible ? "96px minmax(0,1fr)" : "minmax(0,1fr)",
}}
>
{visible && <Avatar />}
<div>
<Nametag />
<Texts />
</div>
</div>
</Dialog>
);
}3. Register your dialog component
Set the component when creating Game, then pass the same instance to GameProviders:
import { Game, GameProviders, Player } from "narraleaf-react";
import { GameDialog } from "./GameDialog";
const game = new Game({ dialog: GameDialog });
export function GameView() {
return (
<GameProviders game={game}>
<Player story={story} onReady={({ liveGame }) => liveGame.newGame()} />
</GameProviders>
);
}4. Per-line and per-sprite rules
One line without a portrait
alice.say("This line hides the avatar.", { avatar: false });Optional: portrait follows the sprite on stage
Tie a different dialog avatar to a stage Image, used while that sprite is visible. See addPortrait on the Character page.
const body = new Image({
name: "alice-body",
src: "/characters/alice/body-normal.png",
});
const alice = new Character("Alice", {
avatar: "/characters/alice/avatar-default.png", // when body is hidden
portraits: [{ image: body, avatar: "/characters/alice/avatar-smile.png" }],
});
scene.action([
alice.say("Off-screen → default avatar"),
body.show(/* ... */),
alice.say("On-stage → smile avatar"),
]);Summary
| Goal | What to do |
|---|---|
| Show a headshot for a named character | Character option avatar or .setAvatar(...) |
| Draw it on screen | Place <Avatar /> inside <Dialog>...</Dialog> |
| Custom dialog | game.configure({ dialog: YourDialog }) |
| Per-line exceptions | { avatar: false }, { avatar: "/other.png" }, or a resolver (see Dialog) |
Multiple visible portraits and tag resolvers are on the Dialog page.