Skip to content

React Components

All components are exported from @doodle-engine/react.

Context provider that wraps the game UI. Holds the engine instance and manages state updates.

import { GameProvider } from '@doodle-engine/react';
<GameProvider engine={engine} initialSnapshot={snapshot}>
{children}
</GameProvider>;
PropTypeDefaultDescription
engineEnginerequiredEngine instance (already initialized)
initialSnapshotSnapshotrequiredInitial snapshot (from newGame or loadGame)
childrenReactNoderequiredChild components
devToolsbooleanfalseEnable window.doodle console API. Pass import.meta.env.DEV.
interface GameContextValue {
snapshot: Snapshot;
actions: {
selectChoice: (choiceId: string) => void;
continueDialogue: () => void;
talkTo: (characterId: string) => void;
travelTo: (locationId: string) => void;
writeNote: (title: string, text: string) => void;
deleteNote: (noteId: string) => void;
setLocale: (locale: string) => void;
saveGame: () => SaveData;
loadGame: (saveData: SaveData) => void;
dismissInterlude: () => void;
};
}

Access via useGame() hook.

Batteries-included full-screen renderer. Provides a complete game UI with all components pre-wired.

import { GameRenderer } from '@doodle-engine/react';
<GameProvider engine={engine} initialSnapshot={snapshot}>
<GameRenderer className="my-game" />
</GameProvider>;
PropTypeDefaultDescription
classNamestring''CSS class
  • Main area: Location view with banner, dialogue box, choices, or character list
  • Sidebar (right): Party portraits and resources (visible variables)
  • Bottom bar: Inventory, Journal, Notes, Map, and Save/Load. Each opens a panel overlay

Requires GameProvider for snapshot and actions. If AudioSettingsProvider is present in the tree, a Settings button appears in the bottom bar with volume sliders. Without the provider, the settings button is hidden.

When used inside GameShell, both providers are already set up. When used standalone, wrap in AudioSettingsProvider if you want the built-in settings panel.

  • Auto-filters underscore-prefixed variables from the Resources panel
  • Shows notifications as transient overlays
  • Settings panel with volume controls (requires AudioSettingsProvider)

Displays the current dialogue node with speaker name, portrait, and text.

import { DialogueBox } from '@doodle-engine/react';
<DialogueBox dialogue={snapshot.dialogue} />;
PropTypeDefaultDescription
dialogueSnapshotDialoguerequiredCurrent dialogue data
classNamestring''CSS class

Displays available dialogue choices as clickable buttons. When there are no choices, renders a Continue button instead.

import { ChoiceList } from '@doodle-engine/react';
<ChoiceList
choices={snapshot.choices}
onSelectChoice={actions.selectChoice}
onContinue={actions.continueDialogue}
continueLabel={snapshot.ui['ui.continue']}
/>;
PropTypeDefaultDescription
choicesSnapshotChoice[]requiredAvailable choices
onSelectChoice(choiceId: string) => voidrequiredChoice selection handler
onContinue() => voidrequiredCalled when player clicks Continue
continueLabelstring'Continue'Label for the Continue button
classNamestring''CSS class

Number keys 1–9 select choices by position. Enter and Space trigger the Continue button when it is shown.

Displays the current location with banner image, name, and description.

import { LocationView } from '@doodle-engine/react';
<LocationView location={snapshot.location} />;
PropTypeDefaultDescription
locationSnapshotLocationrequiredLocation data
classNamestring''CSS class

Displays characters at the current location as clickable cards with portraits.

import { CharacterList } from '@doodle-engine/react';
<CharacterList
characters={snapshot.charactersHere}
onTalkTo={actions.talkTo}
/>;
PropTypeDefaultDescription
charactersSnapshotCharacter[]requiredCharacters to display
onTalkTo(characterId: string) => voidrequiredTalk handler
classNamestring''CSS class

Displays the current in-game time.

import { GameTime } from '@doodle-engine/react';
<GameTime time={snapshot.time} format="narrative" />;
PropTypeDefaultDescription
time{ day: number; hour: number }requiredTime from snapshot
format'numeric' | 'narrative' | 'short''numeric'Display format
classNamestring''CSS class
  • numeric: “Day 3, 14:00”
  • narrative: “Day 3, Afternoon”
  • short: “D3 14:00”

The narrative format uses these time-of-day labels: Dawn (5–7), Morning (8–11), Midday (12–13), Afternoon (14–16), Evening (17–19), Dusk (20–21), Night (22–4).

Displays the map with clickable location markers.

import { MapView } from '@doodle-engine/react';
<MapView
map={snapshot.map}
currentLocation={snapshot.location.id}
onTravelTo={actions.travelTo}
/>;
PropTypeDefaultDescription
mapSnapshotMap | nullrequiredMap data (null hides component)
currentLocationstringCurrent location ID for distance calculation
currentTime{ day: number; hour: number}Current time for arrival calculation
onTravelTo(locationId: string) => voidrequiredTravel handler
confirmTravelbooleantrueShow confirmation dialog before travel
classNamestring''CSS class

When confirmTravel is true and the player clicks a location, a dialog shows the destination name and estimated journey time before any travel occurs. If currentTime is provided, the dialog also shows the expected arrival time. If currentLocation is not provided, the dialog skips the time estimate and just asks for confirmation. Travel time display is approximate. The engine applies its own time advancement rules when travelTo is called.

Displays the player’s items in a grid with click-to-inspect modal.

import { Inventory } from '@doodle-engine/react';
<Inventory items={snapshot.inventory} />;
PropTypeDefaultDescription
itemsSnapshotItem[]requiredInventory items
classNamestring''CSS class
  • Grid layout with item icons
  • Click an item to open inspection modal
  • Modal shows full image, name, description, and close button
  • Click overlay or close button to dismiss

Displays active quests and unlocked journal entries.

import { Journal } from '@doodle-engine/react';
<Journal quests={snapshot.quests} entries={snapshot.journal} />;
PropTypeDefaultDescription
questsSnapshotQuest[]requiredActive quests
entriesSnapshotJournalEntry[]requiredUnlocked journal entries
classNamestring''CSS class
  • Quests shown first with name, description, and current stage
  • Journal entries shown below, with category used as CSS class (journal-category-{category})

Displays player-written notes with a form to add new ones and a delete button per note.

import { PlayerNotes } from '@doodle-engine/react';
<PlayerNotes
notes={snapshot.playerNotes}
onWrite={actions.writeNote}
onDelete={actions.deleteNote}
/>;
PropTypeDefaultDescription
notesPlayerNote[]requiredPlayer-written notes
onWrite(title: string, text: string) => voidrequiredAdd note handler
onDelete(noteId: string) => voidrequiredDelete note handler
classNamestring''CSS class

Notes are stored in game state and persisted through save/load.

Displays transient notifications.

import { NotificationArea } from '@doodle-engine/react';
<NotificationArea notifications={snapshot.notifications} />;
PropTypeDefaultDescription
notificationsstring[]requiredNotification messages
classNamestring''CSS class

Notifications are transient. They appear in one snapshot and are automatically cleared by the engine.

Save and load game state via localStorage.

import { SaveLoadPanel } from '@doodle-engine/react';
<SaveLoadPanel
onSave={actions.saveGame}
onLoad={actions.loadGame}
storageKey="my-game-save"
/>;
PropTypeDefaultDescription
onSave() => SaveDatarequiredSave handler
onLoad(saveData: SaveData) => voidrequiredLoad handler
storageKeystring'doodle-engine-save'localStorage key
classNamestring''CSS class
  • Save button serializes to localStorage
  • Load button disabled when no save exists
  • Shows temporary “Saved!” / “Loaded!” / “No save found” feedback

Full-screen narrative text scene (chapter card, dream sequence, etc.). Displays a background image with auto-scrolling text. Handled automatically by GameRenderer and GameShell. Use this only when building a custom renderer.

import { Interlude } from '@doodle-engine/react';
<Interlude
interlude={snapshot.pendingInterlude}
onDismiss={actions.dismissInterlude}
/>;
PropTypeDescription
interludeSnapshotInterludeInterlude data from the snapshot
onDismiss() => voidCalled when the player skips or finishes reading

The player can dismiss via click, Skip button, Space, Enter, or Escape. Mouse wheel and arrow keys scroll manually and pause auto-scroll.

Fullscreen video/cutscene overlay with a visible Skip button. Also supports skip via keypress (Escape, Space, Enter).

import { VideoPlayer } from '@doodle-engine/react';
<VideoPlayer
src={snapshot.pendingVideo}
onComplete={() => console.log('Video done')}
/>;
PropTypeDefaultDescription
srcstringrequiredVideo file path (resolved by engine)
onComplete() => voidrequiredCalled when video ends or is skipped
classNamestring''CSS class

Image component that integrates with the asset preloading system. Shows a placeholder while the asset loads from the preload cache, then fades in. For custom renderers that need smooth image display without flash.

import { AssetImage } from '@doodle-engine/react';
<AssetImage
src={snapshot.location.banner}
alt={snapshot.location.name}
className="location-banner"
/>;

Extends all standard <img> HTML attributes, plus:

PropTypeDefaultDescription
srcstringrequiredAsset path from snapshot
placeholderstringtransparent 1x1Shown while loading
fadeInnumber200Fade-in duration in ms (0 to disable)

Asset paths from the snapshot are already resolved. Pass them directly.

Progress screen displayed while game assets load. Used as the default renderLoading UI inside GameShell and AssetProvider.

import { LoadingScreen } from '@doodle-engine/react'
<GameShell
renderLoading={(state) => (
<LoadingScreen state={state} background="/assets/images/loading-bg.jpg" />
)}
...
/>
PropTypeDefaultDescription
stateAssetLoadingStaterequiredLoading state from AssetProvider
backgroundstringBackground image URL (from shell.loading.background)
renderProgress(progress: number, phase: string) => ReactNodeCustom progress bar renderer
classNamestring''CSS class

Style it by targeting .loading-screen, .loading-screen-content, .loading-screen-spinner, .loading-screen-phase, .loading-screen-percent, .loading-screen-bar-track, and .loading-screen-bar-fill in your CSS.

Brief studio/logo screen that auto-advances. Assets and duration come from config.shell.splash in game.yaml.

import { SplashScreen } from '@doodle-engine/react';
<SplashScreen
shell={config.shell?.splash}
onComplete={() => setScreen('title')}
/>;
PropTypeDefaultDescription
shellShellConfig['splash']Splash config from game.yaml
onComplete() => voidrequiredCalled when splash finishes
classNamestring''CSS class

Duration defaults to 2000ms if not set in shell.duration. Click anywhere to skip.

The shell config fields:

FieldTypeDescription
logostringLogo image path
backgroundstringBackground image path
soundstringSound effect played on enter
durationnumberAuto-advance time in ms (default 2000)

Main menu with New Game, Continue, and Settings buttons.

import { TitleScreen } from '@doodle-engine/react';
<TitleScreen
title="My Game"
subtitle="A text-based adventure"
hasSaveData={true}
onNewGame={handleNewGame}
onContinue={handleContinue}
onSettings={handleSettings}
/>;
PropTypeDefaultDescription
shellShellConfig['title']Title config from game.yaml
titlestring'Doodle Engine'Game title text (shown when no logo)
subtitlestringSubtitle text
hasSaveDatabooleanrequiredWhether Continue button is shown
onNewGame() => voidrequiredNew Game handler
onContinue() => voidrequiredContinue handler
onSettings() => voidrequiredSettings handler
classNamestring''CSS class

In-game overlay with Resume, Save, Load, Settings, and Quit to Title buttons.

import { PauseMenu } from '@doodle-engine/react';
<PauseMenu
onResume={handleResume}
onSave={handleSave}
onLoad={handleLoad}
onSettings={handleSettings}
onQuitToTitle={handleQuit}
/>;
PropTypeDefaultDescription
onResume() => voidrequiredResume gameplay
onSave() => voidrequiredSave game
onLoad() => voidrequiredLoad saved game
onSettings() => voidrequiredOpen settings
onQuitToTitle() => voidrequiredQuit to title screen
classNamestring''CSS class

Settings UI with volume sliders and language selection.

import { SettingsPanel, useAudioSettings } from '@doodle-engine/react';
const audioSettings = useAudioSettings();
<SettingsPanel
audio={audioSettings}
uiSoundControls={uiSoundControls}
availableLocales={[{ code: 'en', label: 'English' }]}
currentLocale={snapshot.currentLocale}
onLocaleChange={actions.setLocale}
onBack={handleBack}
/>;
PropTypeDefaultDescription
audioSettingsPanelAudiorequiredVolume values and setters
uiSoundControlsUISoundControlsUI sound controls
availableLocales{ code: string; label: string }[]Language options
currentLocalestringCurrent language code
onLocaleChange(locale: string) => voidLanguage change handler
onBack() => voidrequiredBack/close handler
classNamestring''CSS class

SettingsPanelAudio has the same shape as AudioSettings from AudioSettingsContext, so you can pass useAudioSettings() directly as the audio prop.

Complete game wrapper that manages the full lifecycle: splash screen → title screen → gameplay, with pause menu, settings, and video playback built in.

import { GameShell } from '@doodle-engine/react';
<GameShell
registry={registry}
config={config}
manifest={manifest}
title="My Game"
subtitle="A text-based adventure"
availableLocales={[{ code: 'en', label: 'English' }]}
devTools={import.meta.env.DEV}
/>;
PropTypeDefaultDescription
registryContentRegistryrequiredContent registry from /api/content
configGameConfigrequiredGame config from /api/content
manifestAssetManifestrequiredAsset manifest from /api/manifest
assetLoaderAssetLoaderCustom asset loader (for non-browser environments)
titlestring'Doodle Engine'Game title text
subtitlestringSubtitle text
uiSoundsUISoundConfig | falseUI sound config, or false to disable
audioOptionsAudioManagerOptionsCrossfade duration and other audio config
storageKeystring'doodle-engine-save'localStorage key for saves
availableLocales{ code: string; label: string }[]Language options for settings
classNamestring''CSS class
renderLoading(state: AssetLoadingState) => ReactNodeOverride the loading screen
devToolsbooleanfalseEnable window.doodle console API. Pass import.meta.env.DEV.

Splash screen, loading background, title logo, and UI sounds are configured in game.yaml under shell:. See Asset Loading for the full shell config reference.

  • Asset loading with progress screen before any game content renders
  • Splash screen (shown when config.shell.splash is configured)
  • Title screen with New Game, Continue (if save exists), Settings
  • In-game pause menu (Menu button or Escape key)
  • Settings panel with volume sliders and language select
  • Automatic video/cutscene playback from pendingVideo
  • Save/load via localStorage
  • UI click sounds (configurable)