A powerful, reactive text-based game engine for React applications. Build interactive narrative experiences with TypeScript, featuring state management, multiple passage types, and a comprehensive save system.
Building interactive narrative experiences shouldn't require reinventing the wheel. React Text Game provides a production-ready game engine that handles the complexity of state management, save systems, and UI components—so you can focus on crafting your story.
- Zero boilerplate - Start building immediately with factory functions and pre-built components
- Production-ready - Built-in save system with IndexedDB persistence and encrypted exports
- Author-friendly - Write passages in Markdown (MDX) or programmatically with TypeScript
- Fully customizable - Use our UI components or build your own with the core engine
- Type-safe - Catch errors at compile time with comprehensive TypeScript support
- Modern React - Built for React 18+ with hooks and reactive state management
Whether you're building a visual novel, interactive fiction, or educational narrative experience, React Text Game provides the foundation to ship faster.
- Reactive State Management - Built on Valtio for automatic UI updates
- Multiple Passage Types - Story passages, Interactive Maps, and custom Widget passages
- Comprehensive Save System - IndexedDB persistence with encrypted export/import
- Audio System - Full-featured audio management with reactive state, persistence, and global controls
- Themeable UI Components - Tailwind CSS v4 with semantic color tokens
- Internationalization - i18next-powered translations with persistent language switching
- Type-Safe - Full TypeScript support with detailed JSDoc documentation
- Factory-Based Entities - Plain-object factories with optional class-based escape hatches
- React Integration - Built-in hooks and React 19 support
- Modular Architecture - Use core engine independently or with UI components
This monorepo contains three publishable packages:
The core game engine providing:
- Game orchestration and entity registry
- Reactive state management (Valtio)
- JSONPath-based storage system
- Story and Interactive Map passages
- Audio system with reactive state and persistence
- Save/load system with auto-save
- React hooks for game integration
UI components library featuring:
- Pre-built game components (GameProvider, MainMenu, PassageController)
- Story and Interactive Map renderers
- Save/Load modals with slot management
- Tailwind CSS v4 semantic theming
- Dark mode support
MDX integration package enabling:
- Author-friendly Markdown syntax for game passages
- Seamless React component integration in narratives
- Type-safe custom components with full TypeScript support
- Vite plugin for optimized build pipeline
- Automatic metadata and story structure extraction
- Runtime-evaluated dynamic variables
# Install core and UI packages
bun add @react-text-game/core @react-text-game/ui
# Optional: Add MDX support for Markdown-based passages
bun add @react-text-game/mdx @mdx-js/mdx @mdx-js/react
# Or use npm/yarn/pnpm
npm install @react-text-game/core @react-text-game/ui
npm install @react-text-game/mdx @mdx-js/mdx @mdx-js/reactimport { Game, createEntity, newStory } from "@react-text-game/core";
import { GameProvider, PassageController } from "@react-text-game/ui";
import "@react-text-game/ui/styles";
// Initialize the game
await Game.init({
gameId: "my-game",
gameName: "My Adventure",
translations: {
defaultLanguage: "en",
fallbackLanguage: "en",
resources: {
en: {
passages: {
intro: "Welcome to the Game",
},
},
},
},
});
// Create a game entity (factory-first approach)
const player = createEntity("player", {
name: "Hero",
stats: {
health: 100,
mana: 50,
},
inventory: [] as string[],
});
// Create a story passage
const intro = newStory("intro", () => [
{
type: "header",
content: "Welcome to the Game",
props: { level: 1 },
},
{
type: "text",
content: `Hello, ${player.name}!`,
},
{
type: "actions",
content: [
{
label: "Start Adventure",
action: () => Game.jumpTo("chapter-1"),
},
],
},
]);
// React component
function App() {
return (
<GameProvider game={Game}>
<PassageController />
</GameProvider>
);
}
// Start the game
Game.jumpTo(intro);Prefer class-based entities? Extend
BaseGameObjectdirectly—the factory and class APIs work side by side.
React Text Game ships with i18next baked into the core engine and the UI package. The translations option you pass to Game.init accepts the same structure as i18next resources, and the engine automatically persists the player’s language choice in the save database.
- Core exports
useGameTranslationandgetGameTranslationso you can localize passages, UI, and game logic. - The optional UI package exposes a
LanguageTogglecomponent and contributes default English strings under theuinamespace; your resources override them per language. - Supported language codes are derived from your resources, and missing keys fall back to the language you declare in
fallbackLanguage.
import { useGameTranslation } from "@react-text-game/core/i18n";
import { LanguageToggle } from "@react-text-game/ui";
function StatusBar() {
const { t, currentLanguage } = useGameTranslation("common");
return (
<header className="flex items-center gap-4">
<span>
{t("status.currentLanguage", { language: currentLanguage })}
</span>
<LanguageToggle />
</header>
);
}- Full Documentation - Comprehensive guides and tutorials (Docusaurus site)
- Handling Side Effects - Best practices for state modifications in passages
- API Reference - Auto-generated TypeDoc API documentation
- Example Game - Complete game implementation example
This is a Turborepo monorepo using Bun as the package manager.
- Bun 1.2.23 or later
- Node.js 18 or later
# Install dependencies
bun install
# Run all packages in watch mode
bun run dev
# Build all packages
bun run build
# Type checking
bun run check-types
# Linting
bun run lint
# Code formatting
bun run formatTarget specific packages or apps using the --filter flag:
# Run dev for a specific app
bun run dev --filter=example-game
# Run dev for a specific package
bun run dev --filter=@react-text-game/core
# Run multiple packages/apps together (common development workflow)
bun run dev --filter=@react-text-game/core --filter=@react-text-game/ui --filter=example-game
# Build only the core package
bun run build --filter=@react-text-game/core
# Build all publishable packages
bun run build --filter='@react-text-game/*'
# Run typecheck for UI package only
bun run typecheck --filter=@react-text-game/ui
# Run lint for all packages
bun run lint --filter='@react-text-game/*'| Workflow | Command |
|---|---|
| Work on core engine | bun run dev --filter=@react-text-game/core --filter=core-test-app |
| Work on UI components | bun run dev --filter=@react-text-game/core --filter=@react-text-game/ui --filter=ui-test-app |
| Work on MDX package | bun run dev --filter=@react-text-game/core --filter=@react-text-game/mdx --filter=example-game |
| Full stack development | bun run dev --filter=@react-text-game/core --filter=@react-text-game/ui --filter=example-game |
| Documentation site | bun run docs or bun run dev --filter=@react-text-game/docs |
react-text-game/
├── packages/
│ ├── core/ # @react-text-game/core - Game engine
│ ├── ui/ # @react-text-game/ui - UI components
│ └── mdx/ # @react-text-game/mdx - MDX integration
├── apps/
│ ├── docs/ # Docusaurus documentation site
│ ├── example-game/ # Example game implementation
│ ├── core-test-app/ # Core package testing
│ └── ui-test-app/ # UI components testing
└── turbo.json # Turborepo configuration
Note: The apps/ directory contains examples, tests, and documentation. The main deliverables are the packages in packages/.
Contributions are welcome! This section covers how to work effectively with the codebase.
- Fork the repository and clone it locally
- Install dependencies with
bun install - Choose your development workflow based on what you're working on (see table above)
Understanding the dependency graph helps you run only what's needed:
@react-text-game/core (no internal deps)
↑
@react-text-game/ui (depends on core)
↑
@react-text-game/mdx (depends on core)
- Working on core? Run core + a test app to see changes
- Working on UI? Run core + UI + a test app (UI depends on core)
- Working on MDX? Run core + MDX + example-game
Pick the right filter combination for your task:
# Core engine development
bun run dev --filter=@react-text-game/core --filter=core-test-app
# UI components development
bun run dev --filter=@react-text-game/core --filter=@react-text-game/ui --filter=ui-test-app
# Full example game development
bun run dev --filter=@react-text-game/core --filter=@react-text-game/ui --filter=example-game
# Documentation writing
bun run docs- Core package: Use factory functions (
createEntity,newStory,newInteractiveMap) - UI package: Use semantic color tokens (never hardcode colors like
bg-blue-500) - All packages: Write TypeScript with proper types
# Run tests
bun test
# Type checking
bun run typecheck
# Or check specific package
bun run typecheck --filter=@react-text-game/coreBefore submitting, ensure all checks pass:
# Linting
bun run lint
# Type checking
bun run typecheck
# Format code
bun run formatIf you're modifying a publishable package (core, ui, or mdx):
bun run changesetFollow the prompts to describe your changes. This generates version bumps and changelog entries.
- Keep changes focused - One feature or fix per PR
- Update documentation - If adding features, update
apps/docs - Add tests - For new functionality, add tests in the test apps
- Follow semantic versioning - Use changesets to indicate breaking changes
| Task | Command |
|---|---|
| Install dependencies | bun install |
| Start dev (all) | bun run dev |
| Start dev (specific) | bun run dev --filter=<package> |
| Build all | bun run build |
| Run tests | bun test |
| Type check | bun run typecheck |
| Lint | bun run lint |
| Format | bun run format |
| Create changeset | bun run changeset |
MIT (c) laruss
- NPM Packages:
- Repository: GitHub
- Issues: Report bugs and request features on GitHub Issues
