NarraLeaf

Quick Start

Create your first desktop visual novel with NarraLeaf.

NarraLeaf combines a main-process app host, a generated renderer entry, and a NarraLeaf-React story. This guide creates the smallest project shape that the CLI expects.

Create a Project

Initialize the project

Run the CLI init command:

narraleaf init my-visual-novel
cd my-visual-novel

init delegates to the latest NarraLeaf skeleton package and creates the project files for you.

Start the development server

Run the local development command:

narraleaf dev

The dev command watches the main process and renderer process, starts Electron, and asks the renderer to refresh when page files change.

Minimal Project Files

If you are wiring a project manually, NarraLeaf expects this shape:

package.json
narraleaf.config.js
App.tsx

Main Process

Create the Electron app host from AppConfig, then launch the main window after NarraLeaf is ready.

main/index.ts
import { AppConfig } from "narraleaf";

const app = new AppConfig({
    appErrorHandling: "restart",
}).create();

app.onReady(() => {
    void app.launchApp();
});

Renderer App

The renderer app exports metadata.story and a shell component. The shell receives generated pages as children.

renderer/App.tsx
import type { ReactNode } from "react";
import { Character, Scene, Story } from "narraleaf-react";

const story = new Story("First NarraLeaf Story");
const opening = new Scene("opening", {
    background: "#20232a",
});

const narrator = new Character("Narrator");
opening.action([
    narrator.say("Welcome to NarraLeaf."),
]);

story.entry(opening);

export const metadata = {
    story,
};

export default function App({ children }: { children: ReactNode }) {
    return <main>{children}</main>;
}

Home Page

Use renderer hooks from narraleaf/renderer to access the application shell.

renderer/pages/index.tsx
import { useApp, useGamePlayback } from "narraleaf/renderer";

export default function HomePage() {
    const app = useApp();
    const { isPlaying } = useGamePlayback();

    return (
        <button onClick={() => void app.newGame()}>
            {isPlaying ? "Restart" : "Start"}
        </button>
    );
}

Next Steps

On this page