NarraLeaf

Dialog Avatar Tutorial

Show a small portrait next to ADV dialog text with Avatar. Two steps:

  1. Tell the Character which image to use, or bind a stage Image for expression-based portraits.
  2. Render <Avatar /> inside your custom Dialog.

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

GoalWhat to do
Show a headshot for a named characterCharacter option avatar or .setAvatar(...)
Draw it on screenPlace <Avatar /> inside <Dialog>...</Dialog>
Custom dialoggame.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.

On this page