NarraLeaf

NVL Container

The NVL mode components, NvlContainer, DefaultNvlContainer, and NvlDialogList, and the useNvl family of hooks for reading NVL state.

NVL Container component renders the NVL (Novel) mode dialog overlay. It displays multiple dialog entries in a scrollable list. You can use DefaultNvlContainer directly or build a custom one with NvlContainer, Nametag, and Texts.

Example

  1. Import the components
import { useEffect } from "react";
import { DefaultNvlContainer, useGame } from "narraleaf-react";
  1. Use the default component (or customize via game.configure)
// DefaultNvlContainer is the built-in implementation.
// It wraps NvlContainer and renders each dialog with Nametag + Texts.
function App() {
    const game = useGame();

    useEffect(() => {
        game.configure({
            nvlDialog: DefaultNvlContainer,
        });
    }, []);

    return /* ... */
}
  1. Customize with your own component
import {
    NvlContainer,
    Nametag,
    Texts,
    useGame,
    type INvlContainerProps,
} from "narraleaf-react";

function CustomNvlContainer({ dialogs = [] }: INvlContainerProps) {
    return (
        <NvlContainer className="bg-black/80 text-white p-16">
            {dialogs.map((d) => (
                <div key={d.entry.id} className="space-y-2">
                    {d.entry.character && <Nametag entry={d.entry} />}
                    <Texts
                        entry={d.entry}
                        gameState={d.gameState}
                        words={d.words}
                        useTypeEffect={d.useTypeEffect}
                        isActive={d.isActive}
                    />
                </div>
            ))}
        </NvlContainer>
    );
}

function App() {
    const game = useGame();
    useEffect(() => {
        game.configure({ nvlDialog: CustomNvlContainer });
    }, []);
    return /* ... */
}

Components

NvlContainer

NvlContainer renders the outer wrapper for NVL mode. It handles visibility, transitions, and aspect ratio scaling. Its children are the dialog list content.

  • children?: React.ReactNode - The children (dialog list).
  • className?: string - The class name of the container.
  • style?: React.CSSProperties - The style of the container.

DefaultNvlContainer

DefaultNvlContainer is the default slot component. It receives dialogs and renderDialogItem from the player, and renders each dialog with Nametag and Texts. You can pass it to game.configure({ nvlDialog: DefaultNvlContainer }) or use it as a reference for custom implementations.

  • dialogs?: NvlDialogProxy[] - The list of dialog entries (provided by the player).
  • renderDialogItem?: NvlDialogItemRenderer - Optional. Use when you only need to customize each item's layout (e.g. styling) without replacing the whole container.

NvlDialogList

NvlDialogList renders the entries themselves. DefaultNvlContainer uses it, and a custom container built on NvlContainer needs it (or its own equivalent) to draw the list. Each entry is wrapped in the dialog context, so Nametag and Texts rendered inside it read that entry rather than the ADV line.

  • children?: React.ReactNode - A custom item element. It is cloned once per entry with entry and index injected.
  • renderDialogItem?: NvlDialogItemRenderer - Takes precedence over children. Receives { entry, index, isActive, nametag, texts }.
  • className?: string / style?: React.CSSProperties - Applied to the list container.

With neither children nor renderDialogItem, each entry is drawn by DefaultNvlDialogItem.

DefaultNvlDialogItem

The default row: the speaker's name followed by a colon, then the line. Use it as the fallback inside a custom list, or as a reference.

  • entry: NvlDialogEntry - The entry to draw.
  • index: number - Its index in the list.
  • texts?: React.ReactNode - Replaces the rendered line, leaving the nametag alone.
  • className?: string / style?: React.CSSProperties

Props

INvlContainerProps

The props passed to the NVL slot component:

  • dialogs?: NvlDialogProxy[] - Array of dialog entries with evaluated words and state.
  • renderDialogItem?: NvlDialogItemRenderer - Optional extension for custom item layout. Receives { entry, index, isActive, nametag, texts }.

NvlDialogItemRenderProps

Props passed to renderDialogItem:

  • entry: NvlDialogEntry - The dialog entry.
  • index: number - The index in the list.
  • isActive: boolean - Whether this dialog is currently active (typing).
  • nametag: React.ReactNode - Pre-rendered nametag (or null if no character).
  • texts: React.ReactNode - Pre-rendered text content.

Customize item layout with renderDialogItem

When you only need to change how each dialog item looks (e.g. add a border, dim inactive items) without replacing the whole container, wrap DefaultNvlContainer and pass renderDialogItem:

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

// Then configure: game.configure({ nvlDialog: CustomNvlWithRenderer });

Hooks

Four hooks read NVL state. They are context readers with no arguments, and they return the values the NvlProvider the player mounts is holding.

The provider wraps the stage, the dialogs and the NVL overlay — so a custom NVL container, a custom dialog, or anything else the stage renders can call them. A page rendered by the page router sits outside that provider and gets the inert default instead: not active, not visible, no entries.

import { useIsNvlMode, useNvlDialogs } from "narraleaf-react";

function LineCount() {
    const inNvl = useIsNvlMode();
    const dialogs = useNvlDialogs();

    if (!inNvl) return null;
    return <span>{dialogs.length} lines</span>;
}

useNvl

Returns the whole NVL context value.

  • state: NvlState - The full record: active, visible, sessionId, dialogs, options, activeDialogId, phase ("idle" | "typing" | "awaitAdvance"), pendingAdvance, isTyping.
  • dialogs: NvlDialogEntry[] - The entries currently in the list.
  • isActive: boolean - Whether the game is in NVL mode.
  • isVisible: boolean - Whether the NVL layer is on screen. A session stays active while the layer is hidden, so these two are not the same question.
  • transitionOptions: Partial<TransformDefinitions.CommonTransformProps> | null - The transform options of the show or hide transition in effect, or null.

useNvlDialogs

useNvl().dialogs — the entries currently in the list.

useIsNvlMode

useNvl().isActive — whether the game is in NVL mode.

useIsNvlVisible

useNvl().isVisible — whether the NVL layer is on screen.

On this page