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
- Import the components
import { useEffect } from "react";
import { DefaultNvlContainer, useGame } from "narraleaf-react";- 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 /* ... */
}- 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 withentryandindexinjected.renderDialogItem?: NvlDialogItemRenderer- Takes precedence overchildren. 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, ornull.
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.