The Intermediate Representation (IR) is a pure JSON tree structure that sits between JSX parsing and template/client-JS generation. It is JSX-independent — adapters consume IR without any knowledge of the original JSX syntax.
#Pipeline Position
JSX Source → [Phase 1: analyzer + jsx-to-ir] → IR → [Phase 2a: adapter] → Template
→ [Phase 2b: ir-to-client-js] → Client JS#Source
The IR type definitions live in packages/jsx/src/types.ts. All node types, attributes, and metadata are defined in this file.
Key node types:
| Type | Description |
|---|---|
IRElement |
HTML/SVG element |
IRText |
Static text |
IRExpression |
Dynamic expression ({braces}) |
IRConditional |
Branching via ternary or logical expressions |
IRLoop |
List rendering via .map() (including filter/sort) |
IRComponent |
Child component reference |
IRFragment |
JSX fragment (<>...</>) |
IRIfStatement |
Early return within a component body |
IRProvider |
Context Provider |
#Hydration Markers
The slotId and needsScope fields in the IR map to HTML attributes in the rendered template:
| IR Field | HTML Output | Purpose |
|---|---|---|
needsScope: true |
bf-s="ComponentName" |
Component root boundary |
slotId: "0" |
bf="0" |
Reference for interactive elements |
Conditional slotId |
bf-c="1" |
Anchor for conditional branches |
The client runtime uses these markers to locate elements that need hydration without a full DOM traversal.
#Debugging
Pass outputIR: true to output the IR as JSON:
import { compileJSXSync } from '@barefootjs/jsx'
const result = compileJSXSync(source, 'Counter.tsx', {
adapter: new HonoAdapter(),
outputIR: true,
})
// result.ir contains the full ComponentIR
console.log(JSON.stringify(result.ir, null, 2))
// result.additionalFiles includes the *.ir.json file
// e.g., { path: 'Counter.ir.json', content: '...' }