NarraLeaf

Custom NVL Dialog

Choose a Customization Level

NVL (Novel) mode displays multiple dialogue entries in a scrollable list, suitable for novel-style narration. You can replace the default NvlContainer via the nvlDialog option in game.configure to fully customize the NVL dialogue appearance.

There are two useful levels: replace the whole container with NvlContainer, or keep DefaultNvlContainer and change only renderDialogItem.

1. Fully Custom NVL Container

Use NvlContainer as the outer wrapper, receive the dialogs array, and render each dialogue yourself. INvlContainerProps includes dialogs and an optional renderDialogItem.

import {
  NvlContainer,
  Nametag,
  Texts,
  type INvlContainerProps,
} from "narraleaf-react";

function CustomNvlContainer({ dialogs = [] }: INvlContainerProps) {
  return (
    <NvlContainer className="bg-black/80 text-white p-16">
      {/* NvlContainer: handles visibility, transitions, aspect-ratio scaling */}
      {dialogs.map((d) => (
        <div key={d.entry.id} className="space-y-2 mb-4">
          {/* d.entry: NvlDialogEntry with character, sentence, etc. */}
          {d.entry.character && (
            <Nametag entry={d.entry} className="text-amber-400 font-bold" />
          )}
          <Texts
            entry={d.entry}
            gameState={d.gameState}
            words={d.words}
            useTypeEffect={d.useTypeEffect}
            isActive={d.isActive}
          />
        </div>
      ))}
    </NvlContainer>
  );
}

2. Customize Per-Dialogue with renderDialogItem

If you only need to adjust how each dialogue is displayed (e.g. border, opacity), keep using DefaultNvlContainer and pass renderDialogItem to customize the layout:

import {
  DefaultNvlContainer,
  useGame,
  type INvlContainerProps,
} from "narraleaf-react";

function CustomNvlWithRenderer({ dialogs, renderDialogItem }: INvlContainerProps) {
  return (
    <DefaultNvlContainer
      dialogs={dialogs}
      renderDialogItem={({ entry, index, isActive, nametag, texts }) => (
        // nametag, texts: pre-rendered by DefaultNvlContainer
        <div className={`mb-4 ${isActive ? "opacity-100" : "opacity-60"}`}>
          {nametag}
          <div className="border-l-4 border-blue-500 pl-2">{texts}</div>
        </div>
      )}
    />
  );
}

3. renderDialogItem Parameters

renderDialogItem receives NvlDialogItemRenderProps:

ParamTypeDescription
entryNvlDialogEntryDialogue entry (character, sentence, etc.)
indexnumberIndex in the list
isActivebooleanWhether this is the currently active (typing) dialogue
nametagReact.ReactNodePre-rendered character name (null when no character)
textsReact.ReactNodePre-rendered text content

4. Register with Game

import { Game, GameProviders, Player } from "narraleaf-react";

const game = new Game({ nvlDialog: CustomNvlContainer });

function App() {
  return (
    <GameProviders game={game}>
      <Player story={story} onReady={({ liveGame }) => liveGame.newGame()} />
    </GameProviders>
  );
}

Use CustomNvlWithRenderer instead when only the row layout needs to change.

See Also

On this page