Documentation Index Fetch the complete documentation index at: https://mintlify.com/RtlZeroMemory/Rezi/llms.txt
Use this file to discover all available pages before exploring further.
Rezi provides comprehensive debugging tools including record/replay, inspector overlay, debug panel, and deterministic rendering.
Debug Panel
Add a live debug panel to your app:
import { debugPanel } from "@rezi-ui/core" ;
app . view (( state ) => {
return ui . column ({ gap: 1 }, [
debugPanel ({ position: "top-right" }), // FPS counter + render stats
// ... your UI
]);
});
Shows:
FPS (frames per second)
Frame time (ms)
Layout time (ms)
Commit time (ms)
Total render time (ms)
Event count
Position Options
type DebugPanelPosition =
| "top-left"
| "top-right"
| "bottom-left"
| "bottom-right" ;
debugPanel ({ position: "bottom-left" })
Inspector Overlay
Visually inspect widget boundaries and metadata:
import { createAppWithInspectorOverlay } from "@rezi-ui/core" ;
import { createNodeBackend } from "@rezi-ui/node" ;
const backend = await createNodeBackend ();
const { app , inspector } = createAppWithInspectorOverlay ({
backend ,
initialState: { count: 0 },
});
// Toggle inspector with F12
app . keys ( "f12" , () => {
inspector . toggle ();
});
Features:
Widget boundary highlighting
Widget tree visualization
Layout metadata display
Focus state tracking
Mouse hover inspection
Inspector Controls
// Show/hide inspector
inspector . show ();
inspector . hide ();
inspector . toggle ();
// Check state
if ( inspector . isVisible ()) {
console . log ( "Inspector is active" );
}
// Configure
inspector . setOptions ({
showBoundaries: true ,
showMetadata: true ,
highlightFocused: true ,
});
Record and Replay
Capture app execution for deterministic replay:
import { createNodeApp } from "@rezi-ui/node" ;
import { exportReproBundleBytes } from "@rezi-ui/core" ;
import { writeFile } from "fs/promises" ;
const app = createNodeApp ({
initialState: {},
config: {
enableRepro: true , // Enable recording
},
});
// After running the app
const reproBundle = app . exportReproBundle ();
const bytes = exportReproBundleBytes ( reproBundle );
await writeFile ( "session.repro" , bytes );
Replay Recorded Session
import { parseReproBundleBytes , runReproReplayHarness } from "@rezi-ui/core" ;
import { readFile } from "fs/promises" ;
const bytes = await readFile ( "session.repro" );
const bundle = parseReproBundleBytes ( bytes );
if ( ! bundle . ok ) {
console . error ( "Failed to parse repro bundle:" , bundle . error );
process . exit ( 1 );
}
const result = await runReproReplayHarness ({
bundle: bundle . bundle ,
view: myViewFunction ,
onFrame : ( frameNumber , snapshot ) => {
console . log ( `Frame ${ frameNumber } : ${ snapshot . widgets . length } widgets` );
},
});
if ( ! result . ok ) {
console . error ( "Replay failed:" , result . fatal || result . mismatch );
}
Use cases:
Bug reproduction
Performance regression testing
Integration test fixtures
User session replay
Debug Logging
Enable detailed logging:
import { createDebugController } from "@rezi-ui/core" ;
const debugController = createDebugController ({
categories: [ "frame" , "event" , "perf" , "error" ], // What to log
onRecord : ( record ) => {
console . log ( `[ ${ record . category } ] ${ JSON . stringify ( record . payload ) } ` );
},
});
const app = createNodeApp ({
initialState: {},
config: {
debugController , // Attach debug controller
},
});
Debug Categories
Frame rendering events: {
category : "frame" ,
payload : {
frameNumber : 42 ,
widgetCount : 15 ,
renderMs : 2.3 ,
layoutMs : 1.1 ,
}
}
Input events: {
category : "event" ,
payload : {
kind : "key" ,
key : "enter" ,
action : "press" ,
id : "save-button" ,
}
}
Performance metrics: {
category : "perf" ,
payload : {
phase : "layout" ,
durationMs : 1.2 ,
}
}
Runtime errors: {
category : "error" ,
payload : {
code : "ZRUI_INVALID_PROPS" ,
message : "Button requires id prop" ,
stack : "..." ,
}
}
Debug widget tree structure:
import { inspect , debug } from "@rezi-ui/core" ;
const view = ( state ) => {
const tree = ui . column ({ gap: 1 }, [
ui . text ( "Title" ),
ui . button ({ id: "save" , label: "Save" }),
]);
// Log widget tree structure
console . log ( inspect ( tree ));
return tree ;
};
Output:
Column {
gap: 1,
children: [
Text { content: "Title" },
Button { id: "save", label: "Save" },
]
}
Error Handling
Error Boundaries
import { ui } from "@rezi-ui/core" ;
ui . errorBoundary ({
id: "main-boundary" ,
fallback : ( error ) => ui . column ({ gap: 1 , p: 1 }, [
ui . text ( "Something went wrong" , { style: { fg: "error" , bold: true } }),
ui . text ( error . message , { style: { fg: "fg.secondary" } }),
ui . button ({ id: "reload" , label: "Reload" }),
]),
}, [
// Protected content
riskyWidget ( state ),
])
Error boundaries catch errors in child widgets and display fallback UI.
Global Error Handler
app . onError (( error ) => {
console . error ( "App error:" , error );
// Log to file
appendFileSync ( "errors.log" , ` ${ new Date (). toISOString () } ${ error . stack } \n ` );
// Show error dialog
app . update ( s => ({
showErrorDialog: true ,
errorMessage: error . message ,
}));
});
Testing Utilities
Test Renderer
import { createTestRenderer } from "@rezi-ui/core" ;
const renderer = createTestRenderer ({
width: 80 ,
height: 24 ,
});
const tree = ui . column ({ gap: 1 }, [
ui . text ( "Hello" ),
ui . text ( "World" ),
]);
const result = renderer . render ( tree );
console . log ( result . output ); // Rendered text
console . log ( result . widgets ); // Widget tree
console . log ( result . layout ); // Layout metadata
Snapshot Testing
import { captureSnapshot , serializeSnapshot } from "@rezi-ui/core" ;
const snapshot = captureSnapshot ( tree , { width: 80 , height: 24 });
const serialized = serializeSnapshot ( snapshot );
// Compare with golden
expect ( serialized ). toMatchSnapshot ();
Deterministic Rendering
Rezi rendering is deterministic by design:
// Same state + same view = same output (always)
const state = { count: 42 };
const tree = myViewFunction ( state );
const output1 = renderer . render ( tree );
const output2 = renderer . render ( tree );
assert ( output1 . output === output2 . output ); // Always true
Benefits:
Reproducible bugs
Reliable snapshots
Time-travel debugging
Deterministic replay
Frame Timing
const app = createNodeApp ({
initialState: {},
config: {
internal_onRender : ( metrics ) => {
if ( metrics . totalMs > 16.67 ) { // Dropped frame at 60 FPS
console . warn ( `Slow frame: ${ metrics . totalMs . toFixed ( 2 ) } ms` );
console . log ( ` Layout: ${ metrics . layoutMs . toFixed ( 2 ) } ms` );
console . log ( ` Commit: ${ metrics . commitMs . toFixed ( 2 ) } ms` );
console . log ( ` Render: ${ metrics . renderMs . toFixed ( 2 ) } ms` );
}
},
},
});
Layout Profiling
const app = createNodeApp ({
initialState: {},
config: {
internal_onLayout : ( snapshot ) => {
console . log ( `Laid out ${ snapshot . totalWidgets } widgets` );
console . log ( `Dirty widgets: ${ snapshot . dirtyWidgets } ` );
console . log ( `Layout passes: ${ snapshot . passes } ` );
},
},
});
Common Issues
// ❌ Error: Button requires id
ui . button ({ label: "Save" })
// ✅ Fixed
ui . button ({ id: "save" , label: "Save" })
Interactive widgets require unique id props.
Infinite Update Loop
// ❌ Bad: Creates new object every render
const view = ( state ) => {
app . update ( s => ({ ... s , timestamp: Date . now () })); // Infinite loop!
return ui . text ( "Hello" );
};
// ✅ Fixed: Update in event handler
const view = ( state ) => {
return ui . button ({
id: "refresh" ,
label: "Refresh" ,
});
};
app . on ( "event" , ( event , state ) => {
if ( event . action === "press" && event . id === "refresh" ) {
return { timestamp: Date . now () };
}
});
Never call app.update() inside view functions.
Conditional Hook Calls
// ❌ Bad: Conditional hook
const Widget = defineWidget (( props , ctx ) => {
if ( props . enabled ) {
const value = ctx . useState ( 0 ); // Hook called conditionally!
}
});
// ✅ Fixed: Unconditional hook
const Widget = defineWidget (( props , ctx ) => {
const value = ctx . useState ( 0 );
if ( ! props . enabled ) {
return ui . text ( "Disabled" );
}
// Use value...
});
Hooks must be called in the same order every render.
Best Practices
Inspector Overlay Use F12 inspector during development to visualize widget boundaries and debug layout issues.
Record Sessions Enable repro recording for production apps. Recorded sessions are invaluable for debugging reported issues.
Error Boundaries Wrap risky widgets in error boundaries. Prevent one buggy widget from crashing the entire app.
Debug Panel Keep debug panel visible during development. Watch for frame drops and slow renders.
Next Steps
Testing Write automated tests for your TUI components
API Reference Explore the complete API documentation