@rezi-ui/jsx is Rezi’s native JSX runtime. It maps JSX directly to Rezi VNodes and maintains full API parity with ui.*() factory functions from @rezi-ui/core.
Rezi’s JSX has zero React runtime overhead . It’s a pure compile-time transform that generates Rezi VNodes directly.
Installation
Install the JSX package alongside core and node:
npm install @rezi-ui/core @rezi-ui/node @rezi-ui/jsx
TypeScript Configuration
Configure your tsconfig.json to use Rezi’s JSX runtime:
{
"compilerOptions" : {
"jsx" : "react-jsx" ,
"jsxImportSource" : "@rezi-ui/jsx"
}
}
This configuration uses TypeScript’s automatic JSX runtime. You don’t need to manually import anything in your files.
Basic Example
import { createNodeApp } from "@rezi-ui/node" ;
import { Page , Panel , Row , Text , Spacer , Button } from "@rezi-ui/jsx" ;
type State = { count : number };
const app = createNodeApp < State >({ initialState: { count: 0 } });
app . view (( state ) => (
< Page
p = { 1 }
body = {
< Panel title = "Counter" >
< Row gap = { 1 } items = "center" >
< Text variant = "heading" > Count: { state . count } </ Text >
< Spacer flex = { 1 } />
< Button
id = "inc"
label = "+1"
intent = "primary"
onPress = { () => app . update (( s ) => ({ count: s . count + 1 })) }
/>
</ Row >
</ Panel >
}
/>
));
app . keys ({ q : () => app . stop () });
await app . start ();
JSX vs ui.* Factory Functions
Rezi supports two authoring styles. Choose based on your preference:
JSX Style
import { Column , Text , Button } from "@rezi-ui/jsx" ;
function MyView ( state : State ) {
return (
< Column gap = { 1 } >
< Text variant = "heading" > Hello, { state . name } ! </ Text >
< Button id = "submit" label = "Submit" intent = "primary" />
</ Column >
);
}
Factory Function Style
import { ui } from "@rezi-ui/core" ;
function MyView ( state : State ) {
return ui . column ({ gap: 1 }, [
ui . text ( `Hello, ${ state . name } !` , { variant: "heading" }),
ui . button ( "submit" , "Submit" , { intent: "primary" }),
]);
}
Both produce identical VNode trees and have the same performance characteristics.
Component Reference
All 84+ JSX components map 1:1 to ui.* factories:
Layout & Primitives
import {
Box ,
Row ,
Column ,
Grid ,
Spacer ,
Divider ,
Text ,
Center ,
} from "@rezi-ui/jsx" ;
< Box p = { 2 } bg = "surface" >
< Row gap = { 1 } >
< Text > Left </ Text >
< Spacer flex = { 1 } />
< Text > Right </ Text >
</ Row >
< Divider />
< Column gap = { 2 } >
< Text variant = "heading" > Content </ Text >
</ Column >
</ Box >
import {
Button ,
Input ,
Textarea ,
Checkbox ,
Select ,
RadioGroup ,
Slider ,
Field ,
Form ,
} from "@rezi-ui/jsx" ;
< Form >
< Field label = "Name" description = "Your full name" >
< Input id = "name" placeholder = "Enter name..." />
</ Field >
< Field label = "Bio" >
< Textarea id = "bio" rows = { 5 } />
</ Field >
< Checkbox id = "terms" label = "I agree to terms" />
< Button id = "submit" label = "Submit" intent = "primary" />
</ Form >
Navigation & Structure
import {
Page ,
Header ,
Panel ,
Card ,
Tabs ,
Accordion ,
Breadcrumb ,
Sidebar ,
} from "@rezi-ui/jsx" ;
< Page
p = { 1 }
gap = { 1 }
header = { < Header title = "Dashboard" subtitle = "Overview" /> }
body = {
< Column gap = { 2 } >
< Tabs
items = { [
{ id: "overview" , label: "Overview" },
{ id: "details" , label: "Details" },
] }
activeId = "overview"
/>
< Panel title = "Content" >
< Text > Panel content here </ Text >
</ Panel >
</ Column >
}
/>
Overlays & Feedback
import {
Modal ,
Dialog ,
Dropdown ,
CommandPalette ,
ToastContainer ,
Callout ,
Spinner ,
Progress ,
Badge ,
Status ,
} from "@rezi-ui/jsx" ;
<>
< Modal
visible = { state . showModal }
title = "Confirm Action"
onClose = { () => dispatch ({ type: "close-modal" }) }
>
< Text > Are you sure? </ Text >
</ Modal >
< Callout variant = "warning" >
< Text > This action cannot be undone. </ Text >
</ Callout >
< Row gap = { 1 } >
< Badge variant = "success" > Active </ Badge >
< Status status = "online" label = "Connected" />
< Progress value = { 75 } max = { 100 } />
</ Row >
</>
Data Display
import {
Table ,
VirtualList ,
Tree ,
CodeEditor ,
DiffViewer ,
LogsConsole ,
} from "@rezi-ui/jsx" ;
< Table
columns = { [
{ key: "name" , label: "Name" , width: 20 },
{ key: "status" , label: "Status" , width: 10 },
] }
data = { state . items }
onRowPress = { ( row ) => dispatch ({ type: "select" , id: row . id }) }
/>
Visualization
import {
Canvas ,
Image ,
LineChart ,
Scatter ,
Heatmap ,
BarChart ,
Gauge ,
Sparkline ,
} from "@rezi-ui/jsx" ;
< LineChart
width = { 60 }
height = { 20 }
series = { [
{
id: "cpu" ,
label: "CPU %" ,
data: state . cpuHistory ,
color: "#0ea5e9" ,
},
] }
xAxis = { { label: "Time" } }
yAxis = { { label: "Usage" } }
/>
Children Handling
Rezi’s JSX supports both array and single-child patterns:
// Single child
< Column >
< Text > Single child </ Text >
</ Column >
// Multiple children
< Column >
< Text > First </ Text >
< Text > Second </ Text >
< Text > Third </ Text >
</ Column >
// Array children
< Column >
{ items . map (( item ) => (
< Text key = { item . id } > { item . label } </ Text >
)) }
</ Column >
// Fragments
<>
< Text > One </ Text >
< Text > Two </ Text >
</>
Conditional Rendering
Use JavaScript expressions for conditional rendering:
import { show , when , maybe , match } from "@rezi-ui/jsx" ;
// Ternary
< Column >
{ state . loading ? (
< Spinner />
) : (
< Text > Loaded </ Text >
) }
</ Column >
// Logical AND
< Column >
{ state . error && < Callout variant = "danger" > { state . error } </ Callout > }
</ Column >
// Helper functions
< Column >
{ show ( state . loading , < Spinner /> ) }
{ when ( state . error , () => < Callout variant = "danger" > { state . error } </ Callout > ) }
{ maybe ( state . user , ( user ) => < Text > Hello, { user . name } </ Text > ) }
{ match ( state . status , {
idle: < Badge variant = "neutral" > Idle </ Badge > ,
loading: < Spinner /> ,
success: < Badge variant = "success" > Done </ Badge > ,
error: < Badge variant = "danger" > Failed </ Badge > ,
}) }
</ Column >
List Rendering
For efficient list rendering, use the each helper with keys:
import { each } from "@rezi-ui/jsx" ;
< Column >
{ each (
state . items ,
( item ) => item . id ,
( item ) => (
< Row gap = { 1 } >
< Text > { item . name } </ Text >
< Badge > { item . status } </ Badge >
</ Row >
),
) }
</ Column >
Create reusable components with hooks:
import { defineWidget } from "@rezi-ui/jsx" ;
import { Column , Text , Button } from "@rezi-ui/jsx" ;
type CounterProps = {
initialValue ?: number ;
};
const Counter = defineWidget < CounterProps >( "Counter" , ( ctx , props ) => {
const [ count , setCount ] = ctx . useState ( props . initialValue ?? 0 );
return (
< Column gap = { 1 } >
< Text variant = "heading" > Count: { count } </ Text >
< Button
id = { ctx . id ( "inc" ) }
label = "+1"
intent = "primary"
onPress = { () => setCount ( count + 1 ) }
/>
</ Column >
);
});
// Usage
< Counter initialValue = { 10 } />
JSX Pragma (Per-File Override)
If you don’t want to configure JSX globally, use a per-file pragma:
/** @jsxImportSource @rezi-ui/jsx */
import { Column , Text } from "@rezi-ui/jsx" ;
function MyComponent () {
return (
< Column >
< Text > Hello from JSX </ Text >
</ Column >
);
}
API Parity with ui.*
JSX components delegate to ui.*() factories internally. Props map directly:
JSX Factory Equivalent <Text variant="heading">Hi</Text>ui.text("Hi", { variant: "heading" })✓ <Button id="btn" label="Click" intent="primary" />ui.button("btn", "Click", { intent: "primary" })✓ <Row gap={1}><Text>A</Text></Row>ui.row({ gap: 1 }, [ui.text("A")])✓
All props, callbacks, and styling options work identically.
Rezi’s JSX is intentionally thin:
No virtual DOM diffing — JSX compiles to direct VNode calls
No reconciliation overhead — Same performance as ui.* factories
Zero React runtime — Smaller bundle, faster startup
Tree-shaking friendly — Import only the components you use
Mixing JSX and Factory Functions
You can freely mix both styles in the same codebase:
import { ui } from "@rezi-ui/core" ;
import { Column , Text } from "@rezi-ui/jsx" ;
function MyView () {
return (
< Column gap = { 1 } >
< Text > JSX style </ Text >
{ ui . text ( "Factory style" , { variant: "caption" }) }
</ Column >
);
}
Common Patterns
import { Form , Field , Input , Textarea , Button , Actions , Callout } from "@rezi-ui/jsx" ;
function ContactForm ( state : FormState , actions : FormActions ) {
return (
< Form >
< Field label = "Name" error = { state . errors . name } >
< Input
id = "name"
value = { state . name }
onInput = { ( value ) => actions . setField ( "name" , value ) }
/>
</ Field >
< Field label = "Message" error = { state . errors . message } >
< Textarea
id = "message"
value = { state . message }
rows = { 5 }
onInput = { ( value ) => actions . setField ( "message" , value ) }
/>
</ Field >
{ state . submitError && (
< Callout variant = "danger" > { state . submitError } </ Callout >
) }
< Actions >
< Button id = "cancel" label = "Cancel" onPress = { actions . cancel } />
< Button
id = "submit"
label = "Submit"
intent = "primary"
disabled = { ! state . isValid }
onPress = { actions . submit }
/>
</ Actions >
</ Form >
);
}
Multi-Screen App with Routing
import { Page , Header , Tabs } from "@rezi-ui/jsx" ;
import { match } from "@rezi-ui/jsx" ;
function App ( state : AppState , actions : AppActions ) {
return (
< Page
p = { 1 }
gap = { 1 }
header = { < Header title = "My App" subtitle = "Dashboard" /> }
body = {
<>
< Tabs
items = { [
{ id: "home" , label: "Home" },
{ id: "settings" , label: "Settings" },
{ id: "logs" , label: "Logs" },
] }
activeId = { state . route }
onSelect = { ( id ) => actions . navigate ( id ) }
/>
{ match ( state . route , {
home: < HomeScreen /> ,
settings: < SettingsScreen /> ,
logs: < LogsScreen /> ,
}) }
</>
}
/>
);
}
Next Steps
Widget Catalog Browse all available JSX components
defineWidget Guide Create custom reusable components
Animation Hooks Add transitions and springs to JSX components
Example Templates See JSX in action in template projects