The architectural split in a dafny-replay React app is deliberate: Dafny owns state transitions and invariant enforcement, React owns rendering and user interaction. React’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/metareflection/dafny-replay/llms.txt
Use this file to discover all available pages before exploring further.
useState holds the Dafny History object as an opaque value. Every state change goes through App.Dispatch, App.Undo, or App.Redo — functions that are proved to preserve your domain’s invariant. React never mutates state directly; it only calls the verified transition functions and re-renders when they return a new history.
Prerequisites
- A verified Dafny domain module (see domain obligations)
- A compiled
.cjsbundle and generatedapp.jsfromdafny2js(see dafny2js) - A Vite React app created with
npm create vite@latest my-app -- --template react
Install dependencies
bignumber.js for arbitrary-precision integers. You must install it in your Vite app so the require stub in app.js can resolve it.
Configure Vite
Editvite.config.js to tell Vite’s optimizer to pre-bundle bignumber.js:
optimizeDeps.include, Vite may fail to pre-bundle bignumber.js correctly when it is loaded indirectly through the new Function evaluator that app.js uses to bootstrap the Dafny runtime.
Loading the Dafny bundle
The generatedapp.js handles loading automatically. Behind the scenes it uses Vite’s ?raw import to load the .cjs bundle as a string, then evaluates it with a minimal require stub that supplies bignumber.js:
dafny2js generates it. You just need to ensure bignumber.js is installed and the Vite config is correct.
The
MODULO_MODE: BigNumber.EUCLID setting is required because Dafny’s modulo operation follows Euclidean division semantics, which differs from JavaScript’s % operator for negative numbers.The useState History pattern
Store the DafnyHistory object in React state. Every user action creates a new history by calling the appropriate App function, then updates state with setH:
useState(() => App.Init())uses a lazy initializer soApp.Init()is only called once.App.Present(h)extracts the current model value from the history object for rendering.App.CanUndo(h)andApp.CanRedo(h)return booleans used to disable buttons. These are simple checks on whetherh.pastorh.futureare non-empty.- Every action (
Dispatch,Undo,Redo) returns a brand-newHistoryobject. React sees a new reference and re-renders.
Using Present, CanUndo, and CanRedo
These three accessors cover the most common rendering needs.- Reading state
- Guarding undo/redo
- Complex models (Kanban)
BigNumber handling
Dafny integers compile toBigNumber values. The generated app.js converts them to plain JavaScript numbers automatically wherever the model surface returns an integer. For example, App.Present(h) returns a plain number, not a BigNumber.
If you use app-extras.js or access _internal modules directly, you may encounter raw BigNumber values. Convert them with .toNumber():
Kanban board example
The Kanban app demonstrates a richer domain with a complex model, multiple action types, and model accessors used in JSX:useState holds the history, App.Present extracts the model, and every mutation goes through App.Dispatch. The difference is that the Kanban model is a record with cols, lanes, wip, and cards fields, and AppCore exposes accessors (GetCols, GetLane, GetWip, GetCardTitle) that shield React from the internal Dafny map types.
Running the app
http://localhost:5173. Vite’s hot-module replacement works normally — edits to .jsx files reload instantly without re-evaluating the Dafny bundle.
dafny2js tool
Generate app.js from your Dafny source files.
app-extras.js pattern
Add convenience wrappers on top of the generated API.