A React runtime for Dear ImGui powered by Static Hermes. Write your ImGui interfaces using React's declarative component model and JSX, with full support for hooks, state management, and event handling.
This project demonstrates how Static Hermes seamlessly bridges JavaScript and C++ ecosystems—combining React's modern development experience with the performance and directness of Dear ImGui and Sokol.
Key highlights:
- Fully native in Release mode - Zero runtime dependencies, instant startup, single 5.2MB executable
- JavaScript-first implementation - ~2,400 lines of hand-written JS, only ~700 lines of C++ glue code
- Native Dear ImGui components - Not web-style HTML/CSS or React Native primitives, but direct ImGui widgets
- Zero-cost FFI - ~12,000 lines of auto-generated typed JavaScript bindings for Dear ImGui's C API
While this is an experimental project (not production-ready), it's a fully functional runtime you can build upon. Whether you're interested in learning about React reconcilers, exploring Static Hermes capabilities, or just building quick ImGui tools with React, this project has you covered.
Contributions welcome! If you find bugs, have suggestions, or want to add features, please open an issue or pull request. This project was built by a C/C++ systems developer (the architect of Hermes) with extensive assistance from Claude Code for the JavaScript and React implementation—so there's definitely room for improvement from experienced React developers!
Note: This project is an independent experiment and is not affiliated with, endorsed by, or officially associated with React, Dear ImGui, or their respective teams.
- Features
- What is Static Hermes?
- Platform Support & Requirements
- Examples
- Creating Your Own App
- Supported Components
- Architecture
- Build System
- License
- ✅ Fully native compilation - Release builds are standalone executables with zero dependencies and instant startup
- Showcase example: 5.2MB total (3MB Hermes VM, 1.8MB React native code, rest is app/renderer/ImGui)
- No JavaScript files, no bytecode, no interpreter at runtime
- Lightning-fast startup - no parsing, no JIT warmup, just native code execution
- Single executable distribution
- ✅ React 19.2.0 with custom reconciler
- ✅ Static Hermes with zero-overhead FFI to Dear ImGui
- ✅ Event loop with setTimeout, setImmediate, and Promise support
- ✅ React hooks (useState, useEffect, etc.)
- ✅ Multiple windows with independent state
- ✅ Controlled/uncontrolled window positioning and sizing
- ✅ Custom drawing with shapes (rectangles, circles)
- ✅ Tables with live-updating data
- ✅ Fullscreen root canvas for background elements and overlays
- ✅ Three-unit architecture separating event loop, React logic, and ImGui rendering
Hermes is a compact (~3MB) JavaScript engine designed for mobile and embedded environments. Unlike traditional JavaScript engines that rely heavily on JIT compilation, Hermes focuses on ahead-of-time (AOT) compilation to bytecode, enabling extremely fast startup times and low memory footprint—ideal for resource-constrained devices.
Static Hermes extends Hermes with two powerful capabilities:
- Optional sound typing - Add type annotations to JavaScript for static type checking
- Native code compilation - Compile both typed and untyped JavaScript to native machine code
Three compilation modes:
- Typed mode → Compiles type-annotated JavaScript to native code with zero-overhead FFI to C/C++
- Untyped mode → Compiles standard JavaScript to Hermes bytecode (or native code)
- Source mode → Runs JavaScript directly from source (parsing + JIT)
Zero-cost FFI: In typed mode, JavaScript functions can call C functions with no overhead—no marshaling, no type conversion, no copying. Calls compile down to direct native function calls, making it perfect for integrating JavaScript with C/C++ libraries like Dear ImGui and Sokol.
This project uses typed mode for the ImGui FFI layer (lib/imgui-unit/) and untyped mode for the React runtime and application code. All units communicate seamlessly via globalThis, demonstrating Static Hermes's ability to mix typed and untyped code in a single application.
- macOS ✅ Fully tested and supported
- Linux 🔨 Expected to work with minimal changes (untested)
- Windows ⏳ Coming soon (waiting for Static Hermes Windows support)
You'll need:
- Node.js and npm - For esbuild bundler and React dependencies
- macOS: brew install node or download from nodejs.org
- Linux: apt-get install nodejs npm or yum install nodejs npm
- C++ Compiler
- Clang (recommended) - Officially supported by Static Hermes
- GCC also works but Clang is the tested configuration
- macOS: Install Xcode Command Line Tools or full Xcode
- Linux: apt-get install clang or yum install clang
- CMake (3.20 or later)
- macOS: brew install cmake
- Linux: apt-get install cmake or yum install cmake
- Ninja - Fast build system
- macOS: brew install ninja
- Linux: apt-get install ninja-build or yum install ninja-build
That's it! The project has no other dependencies. The CMake build process automatically downloads and builds Static Hermes on first configure.
You should see the showcase window with multiple demos, background decorations, and interactive controls!
The project includes three example applications that demonstrate different features.
Location: examples/hello/
The simplest possible example—just "Hello World!" text rendered in a fullscreen root window. Perfect for verifying your setup works and understanding the minimal structure.
Run it:
Location: examples/showcase/
A comprehensive demo showing off most features:
- Multiple independent windows with separate state
- React hooks (useState, useEffect)
- Event handlers (button clicks)
- Live-updating table with 40 rows × 8 columns of random stock prices
- Physics simulation (bouncing ball) with custom drawing primitives
- Controlled vs uncontrolled window positioning patterns
- Background decorations using the <root> component
- Status bars and overlays rendered in the root canvas
The showcase includes four components:
- StockTable.jsx - Demonstrates table rendering with live data updates using setInterval
- BouncingBall.jsx - Shows physics simulation with <rect> and <circle> primitives
- ControlledWindow.jsx - Illustrates controlled window positioning with state updates
- Main App - Status bar, background shapes, and two counter windows with buttons
Run it:
Location: examples/dynamic-windows/
Demonstrates dynamic creation and destruction of windows at runtime:
- Adding windows by clicking a button
- Removing windows via the close button (X)
- React list rendering with key prop for stable identity
- Window lifecycle management with state arrays
Run it:
Creating a new React + ImGui application is straightforward with the add_react_imgui_app() CMake function.
app.jsx:
index.js:
myapp.cpp:
That's it! The PROVIDE_IMGUI_MAIN macro tells the runtime to provide a default main function that automatically loads and runs your React bundle.
CMakeLists.txt:
Add to examples/CMakeLists.txt:
That's it! The build system automatically:
- Collects all *.jsx and *.js files
- Bundles with esbuild (JSX transpilation, module resolution)
- Compiles based on REACT_BUNDLE_MODE (native/bytecode/source)
- Links with all required libraries
These components map directly to native Dear ImGui widgets—not web-style HTML/CSS elements or React Native primitives. Each component calls Dear ImGui's C API through zero-cost FFI, giving you the full power and flexibility of ImGui.
This is a demo project, so only a representative subset of ImGui's widgets have been implemented. However, adding new components is straightforward—see Adding New Components below.
All components use lowercase names in JSX (e.g., <window>, <button>). React treats lowercase as host primitives, uppercase as component references.
Creates a fullscreen, transparent window covering the entire viewport. Perfect for background elements, overlays, and status bars that should appear behind or above all other windows.
Props: None
Special Behaviors:
- Always matches viewport size automatically
- Transparent background (no visible window)
- Cannot be moved or resized
- Never brought to front on focus
- Only one <root> per app (warning logged if multiple detected)
Example:
Creates a standard ImGui window that can be moved, resized, and closed.
Props:
- Position (Controlled): x, y - Position enforced every frame
- Position (Uncontrolled): defaultX, defaultY - Initial position only
- Size (Controlled): width, height - Size enforced every frame
- Size (Uncontrolled): defaultWidth, defaultHeight - Initial size only
- title - Window title (default: "Window")
- flags - ImGui window flags as integer (default: 0)
- onWindowState - Callback (x, y, width, height) when position/size changes
- onClose - Callback when close button (X) is clicked. Presence of this prop enables the close button.
Special Behaviors:
- Controlled props (x/y/width/height) are read back from ImGui each frame and fire onWindowState if changed
- Warns if both controlled and uncontrolled props are mixed
- Use controlled props for programmatic window management
- Use uncontrolled props for user-movable windows with initial placement
Example:
Creates a scrollable sub-region within a window.
Props:
- width, height - Dimensions (0 = auto-size)
- noPadding - Remove padding (boolean)
- noScrollbar - Disable scrollbar and scroll with mouse (boolean)
Example:
Renders text with optional styling.
Props:
- color - Text color as hex string ("#RRGGBB" or "#RRGGBBAA") or object {r, g, b, a} (values 0-255)
- disabled - Render as disabled/grayed out (boolean)
- wrapped - Enable text wrapping (boolean)
Children: Only accepts text children (concatenated into a single string)
Example:
Renders a horizontal separator line.
Props: None
Example:
Clickable button with an event handler.
Props:
- onClick - Callback function fired when button is clicked
Children: Only accepts text children (concatenated for button label)
Example:
Places the next item on the same line as the previous item, rather than starting a new line.
Props: None
Example:
Groups elements together visually. Useful for spacing and organization.
Props: None
Example:
Indents all children by one level.
Props: None
Example:
Creates a collapsible section header. Children are only rendered when expanded.
Props:
- title - Header text (default: "Section")
Example:
Tables in ImGui require a specific structure. Use <table> as the container, set up columns with <tablecolumn>, show headers with <tableheader>, and render data with <tablerow> and <tablecell>.
Creates a table container.
Props:
- columns - Required. Number of columns (must be > 0)
- id - Table ID string (default: "table")
- flags - ImGui table flags (default: resizable)
Example structure:
Configures a table column. Must be used before <tableheader>.
Props:
- label - Column header text (default: "")
- flags - ImGui column flags (default: none)
- width - Column width (default: 0 for auto)
Renders the table header row using the column configurations.
Props: None
Starts a new table row.
Props:
- flags - Row flags (default: 0)
- minHeight - Minimum row height (default: 0)
Renders content in a specific column.
Props:
- index - Column index (0-based)
Children: Any components
Full example:
These components use ImGui's DrawList API to render shapes directly. Coordinates are relative to the window's content area (not screen coordinates).
Draws a rectangle.
Props:
- x, y - Position relative to window content area (default: 0)
- width, height - Dimensions (default: 100)
- color - Fill/stroke color as hex string or object (default: white)
- filled - Draw filled (true) or outline (false) (default: true)
Example:
Draws a circle.
Props:
- x, y - Center position relative to window content area (default: 50)
- radius - Circle radius (default: 10)
- color - Fill/stroke color (default: white)
- filled - Draw filled (true) or outline (false) (default: true)
- segments - Number of segments for smoothness (default: 12, higher = smoother)
Example:
Drawing in <root> vs <window>:
When using drawing primitives in a <root> component, coordinates are relative to the screen/viewport, making them perfect for background decorations:
Adding new Dear ImGui components is straightforward. You only need to modify lib/imgui-unit/renderer.js:
1. Add a render function for your component:
2. Add a case to the switch statement in renderNode():
3. Use it in your React components:
Tips for implementing components:
- Use allocTmp() for temporary buffers (ImVec2, ImVec4, etc.) - they're automatically reused across renders
- Check Dear ImGui documentation at imgui.h for available functions and parameters
- FFI bindings are in js_externs.js - all ImGui functions are prefixed with _ig (e.g., _igButton, _igText)
- Handle callbacks safely with safeInvokeCallback() for exception handling
- Validate numeric props with validateNumber() to handle NaN/Infinity
- Parse colors with parseColorToImVec4() or parseColorToABGR() for consistent color handling
The FFI layer provides access to the entire Dear ImGui API, so you can expose any widget you need!
Static Hermes has two compilation modes that cannot be mixed in a single unit:
- Untyped mode: Standard JavaScript (React, reconciler, app code)
- Typed mode: Type-annotated code with zero-cost FFI (required for ImGui bindings)
To work around this limitation and provide a proper event loop, we use a three-unit architecture:
Load order: jslib → React → ImGui
This ensures timer APIs are available for all subsequent code.
Communication: All units communicate through globalThis:
- jslib unit exposes setTimeout, setImmediate, console.log, etc.
- React unit builds component tree and exposes globalThis.reactApp.rootChildren
- ImGui unit reads the tree from globalThis.reactApp.rootChildren and renders it
Provides a browser-like event loop with task scheduling:
- Timer APIs: setTimeout, clearTimeout, setImmediate, clearImmediate, setInterval, clearInterval
- Task queue: Sorted by deadline for efficient scheduling
- Console: console.log, console.error, console.debug
- Environment: process.env.NODE_ENV
- C++ helpers: peekMacroTask() and runMacroTask() for integration
Compilation: Untyped mode with -Xes6-block-scoping
Implements React's reconciler interface to build an in-memory component tree:
- TreeNode class: Represents component instances with unique ID, type, props, children
- TextNode class: Represents text content
- Host config: Implements createInstance, appendChild, commitUpdate, etc.
- Render API: createRoot() and render(element, root)
The reconciler builds plain JavaScript objects in memory. It doesn't know anything about ImGui—that's the renderer's job.
Compilation: Untyped mode, bundled with esbuild alongside React
Provides zero-cost FFI bindings and the rendering pipeline:
- js_externs.js: 500KB+ of auto-generated FFI declarations for all ImGui functions
- renderer.js: Traverses the React tree and calls ImGui FFI functions
- main.js: Sokol callbacks (on_init, on_frame, on_event)
- Helper utilities: Color parsing, number validation, safe callback invocation
Each frame, the renderer:
- Validates single <root> component (if any)
- Traverses globalThis.reactApp.rootChildren
- For each TreeNode, pushes unique ID onto ImGui's ID stack
- Calls appropriate ImGui functions based on component type
- Recursively renders children
- Pops ID from stack
Compilation: Typed mode with -typed flag (required for FFI)
Provides the C++ glue layer:
- Hermes runtime initialization: With microtask queue for Promises
- Event loop integration: Calls peekMacroTask() and runMacroTask() each frame
- Unit loading: Handles native/bytecode/source bundle loading
- Sokol lifecycle: app_init(), app_frame(), app_event(), app_cleanup()
- Memory-mapped file loading: Efficient bundle loading via mmap
- Host functions: performance.now() for high-resolution timing
Note: Applications link only against imgui-runtime, which transitively links all Hermes libraries.
Here's how a React render flows through the system:
Key insight: React maintains component identity across renders. The same TreeNode instance is reused when a component updates (not recreated). Each TreeNode has a unique ID assigned at creation time, which is used for ImGui's ID stack. This ensures ImGui widget state (hover, click, focus) correctly tracks across frames, even when multiple components have identical labels (e.g., multiple "Delete" buttons).
The build system is designed to be simple and automatic:
Root CMakeLists.txt:
- Includes cmake/HermesExternal.cmake for automatic Hermes build
- Includes cmake/react-imgui.cmake for the add_react_imgui_app() function
- Defines REACT_BUNDLE_MODE (0/1/2) based on build configuration
- Automatically collects reconciler files with file(GLOB ... CONFIGURE_DEPENDS)
- Sets up Hermes include directories
Library Units (lib/*/CMakeLists.txt):
- Each unit (jslib, imgui, imgui-runtime) has its own CMakeLists.txt
- Builds to a static library (.a)
- Compiled once, reused across all applications
Application CMake (examples/*/CMakeLists.txt):
- Just 5 lines using add_react_imgui_app():
add_react_imgui_app( TARGET myapp ENTRY_POINT index.js SOURCES myapp.cpp )
The function automatically:
- Collects all *.jsx and *.js files in the current directory
- Bundles with esbuild (JSX transpilation, module resolution)
- Compiles based on REACT_BUNDLE_MODE
- Creates executable with proper linking
The REACT_BUNDLE_MODE determines how React code is compiled:
- Uses shermes to compile JavaScript → native .o object file
- Slowest build time (full ahead-of-time compilation)
- Instant startup - no parsing, no JIT warmup, just execute
- Zero runtime dependencies - React, app code, and runtime all compiled to machine code
- Bundle is statically linked into executable
- Production distribution - single executable, no external files needed
- Example: Showcase app is 5.2MB standalone binary
- Uses hermes compiler to generate .hbc bytecode
- Medium build time
- Medium runtime speed (bytecode VM)
- Requires .hbc file at runtime alongside executable
- Bundle loaded at runtime via evaluateJavaScript()
- No compilation, uses source .js bundle directly
- Fastest build time (just bundling)
- Slowest runtime (parsing + JIT)
- Requires .js file at runtime alongside executable
- Bundle loaded at runtime
- Best for development (fast iteration)
Override the mode explicitly:
Hermes is automatically cloned and built as part of the CMake configuration—no manual setup required:
- First configure: Hermes cloned from GitHub and built (takes several minutes)
- Subsequent builds: Reuse existing Hermes build
- Version control: Set via HERMES_GIT_TAG (default: specific commit hash)
cmake -B build -DHERMES_GIT_TAG=abc123def
- Always Release mode: Hermes always builds in Release for optimal performance
- Per-config isolation: Debug and Release builds get separate Hermes clones
Release builds use:
- REACT_BUNDLE_MODE=0 (native compilation)
- Static linking with Hermes libraries
- Optimized for performance and single-binary distribution
Important: Use cmake --build consistently. Don't alternate between cmake --build and running ninja directly—different ninja versions can trigger spurious rebuilds.
This project is licensed under the MIT License.
Copyright (c) 2025 Tzvetan Mikov
See the LICENSE file for full details.
This project uses the following open-source libraries, each with their own licenses:
- React - MIT License
- Dear ImGui - MIT License
- Hermes - MIT License
- Sokol - zlib/libpng License
Contributions are welcome and encouraged! Whether you're fixing bugs, improving documentation, adding new components, or optimizing performance, your help is appreciated.
Ways to contribute:
- Report bugs - Open an issue with a clear description and reproduction steps
- Suggest features - Propose new components or improvements
- Submit pull requests - Fix bugs, add features, improve documentation
- Share examples - Show what you've built with imgui-react-runtime
- Improve React code - The author is a C/C++ systems developer, so React experts can definitely help improve the reconciler implementation!
Before submitting major changes, please open an issue to discuss your approach.
This project was developed by Tzvetan Mikov (architect of Hermes) with extensive assistance from Claude Code. As a C/C++ systems software developer, the author relied heavily on Claude Code for the JavaScript implementation, React reconciler architecture, and modern JS tooling (esbuild, npm, etc.). The collaboration demonstrates how AI coding assistants can help developers work effectively across different language ecosystems.
Disclaimer: This is an experimental project, not production-ready software. Use at your own risk. That said, we hope you find it interesting and useful for your own explorations! 🚀
This project is an independent experiment and is not affiliated with, endorsed by, or officially associated with React, Dear ImGui, or their respective teams.
.png)


