Documentation
Custom Dialog

Custom Dialog

Overview

The Dialog component renders character dialogue in Say mode, including the character nametag and text content. You can replace the default Dialog component via game.configure to fully customize the dialog appearance.

This guide shows how to create a custom dialog and use Nametag and Texts to adjust the nametag and text layout.

1. Create the Custom Dialog Component

Use Dialog, Nametag, and Texts to build the layout. Dialog is the container, Nametag renders the character name, and Texts renders the dialogue text.

import { Dialog, Nametag, Texts } from "narraleaf-react";
 
function GameDialog() {
  return (
    <Dialog
      className="bg-black/70 rounded-lg px-6 py-4"
      // Dialog: container for the dialog box, supports className/style
    >
      <Nametag className="text-lg font-bold text-amber-400" />
      {/* Nametag: character name, color is defined in character config */}
      <Texts className="text-white text-base leading-relaxed" />
      {/* Texts: dialogue content, inherits from word/sentence config */}
    </Dialog>
  );
}

2. Use useDialog for State (Optional)

If you need to read the current dialogue state (e.g. done, text, isNarrator) in a child component, use the useDialog hook:

import { Dialog, Nametag, Texts, useDialog } from "narraleaf-react";
 
function GameDialog() {
  const { done, text, isNarrator } = useDialog();
  // done: whether typing is complete
  // text: current displayed text
  // isNarrator: true when speaker is null (narrator)
 
  return (
    <Dialog className="bg-black/70 rounded-lg px-6 py-4">
      {!isNarrator && <Nametag className="text-lg font-bold" />}
      {/* Hide nametag when narrator speaks */}
      <Texts className="text-white" />
    </Dialog>
  );
}

3. Register in App

Get the game instance via useGame and register the custom component in useEffect with game.configure:

import { useEffect } from "react";
import { useGame, Player } from "narraleaf-react";
import GameDialog from "./GameDialog";
 
function App() {
  const game = useGame();
 
  useEffect(() => {
    game.configure({
      dialog: GameDialog,  // Replace default dialog with custom one
    });
  }, [game]);
 
  return (
    <Player>
      {/* ... */}
    </Player>
  );
}

4. Full Example (from narraleaf-react-skeleton (opens in a new tab))

This example uses useDialog done for a typing-complete indicator, isNarrator to hide the nametag, and background images:

import { Dialog, Nametag, Texts, useDialog } from "narraleaf-react";
import clsx from "clsx";
 
// Sub-component: show triangle/underline when typing is done
function SentenceContext() {
  const { done } = useDialog();
  return (
    <>
      <Texts className="text-[22px] max-w-max flex items-center" />
      <div className="flex flex-col items-center">
        <div className={clsx(
          "w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[10px] border-t-white",
          done ? "opacity-100" : "opacity-0"  // Show indicator when done
        )} />
        <div className="w-[12px] h-[2px] bg-white mt-[2px]" />
      </div>
    </>
  );
}
 
export function GameDialog() {
  const { isNarrator } = useDialog();
 
  return (
    <Dialog
      className="absolute bottom-4 left-1/2 -translate-x-1/2 p-12 px-16 w-[90%] h-[216px]"
      style={{
        backgroundImage: "url('/ui/game-dialog.png')",
        backgroundSize: "contain",
        backgroundPosition: "bottom",
        backgroundRepeat: "no-repeat",
      }}
    >
      <div className={clsx("absolute left-[30px] -top-[15px]", { "hidden": isNarrator })}>
        <Nametag
          className="px-4 py-2 min-w-[220px] min-h-[56px] flex items-center justify-center"
          style={{
            backgroundImage: "url('/ui/game-dialog-nametag.png')",
            backgroundSize: "contain",
            backgroundPosition: "center",
            backgroundRepeat: "no-repeat",
          }}
        />
      </div>
      <div className="flex items-center gap-[5px] h-full">
        <SentenceContext />
      </div>
    </Dialog>
  );
}

You can also set defaultTextColor and defaultNametagColor when registering:

game.configure({
  dialog: GameDialog,
  defaultTextColor: "white",
  defaultNametagColor: "#2987a1",
});

5. Simple Style Example

function GameDialog() {
  return (
    <Dialog
      style={{
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        borderRadius: "12px",
        padding: "24px",
        border: "1px solid rgba(255, 255, 255, 0.2)",
      }}
    >
      <Nametag style={{ marginBottom: "8px", paddingBottom: "4px", borderBottom: "2px solid rgba(255, 193, 7, 0.8)" }} />
      <Texts />
    </Dialog>
  );
}

Notes

  • Nametag color: Controlled by nametagColor in Character config; avoid overriding with CSS.
  • Text style: Controlled by Word / Sentence config; you can customize background, font, size, etc. for attributes not predefined.