Generates Hono JSX (.tsx) files from the compiler's IR. Works with Hono and any JSX-compatible TypeScript backend.
npm install @barefootjs/hono#Basic Usage
import { compile } from '@barefootjs/jsx'
import { HonoAdapter } from '@barefootjs/hono'
const adapter = new HonoAdapter()
const result = compile(source, { adapter })
// result.template → .tsx file content
// result.clientJs → .client.js file content#Options
const adapter = new HonoAdapter({
clientJsBasePath: '/static/components/',
barefootJsPath: '/static/components/barefoot.js',
clientJsFilename: 'my-component.client.js',
})| Option | Type | Default | Description |
|---|---|---|---|
clientJsBasePath |
string |
'/static/components/' |
Base path for client JS files |
barefootJsPath |
string |
'/static/components/barefoot.js' |
Path to the BarefootJS runtime |
clientJsFilename |
string |
'{componentName}.client.js' |
Override the client JS filename |
#Output Format
#Server Component
Without "use client", the template is generated with props access and hydration markers (for potential parent hydration), but no client JS:
Source:
export function Greeting(props: { name: string }) {
return <h1>Hello, {props.name}!</h1>
}Output (.tsx):
import { bfText, bfTextEnd } from '@barefootjs/hono/utils'
export function Greeting(__allProps: { name: string } & { __instanceId?: string; ... }) {
const { __instanceId, ..., ...props } = __allProps
const __scopeId = __instanceId || `Greeting_${...}`
return (
<h1 bf-s={...} bf="s1">
Hello, {bfText("s0")}{props.name}{bfTextEnd()}!
</h1>
)
}#Client Component
Source:
"use client"
import { createSignal } from '@barefootjs/client'
export function Counter(props: { initial?: number }) {
const [count, setCount] = createSignal(props.initial ?? 0)
return (
<div>
<span>Count: {count()}</span>
<button onClick={() => setCount(n => n + 1)}>+1</button>
</div>
)
}Output (.tsx):
import { bfText, bfTextEnd } from '@barefootjs/hono/utils'
export function Counter(__allProps: { initial?: number } & { __instanceId?: string; ... }) {
const { __instanceId, ..., ...props } = __allProps
const __scopeId = __instanceId || `Counter_${Math.random().toString(36).slice(2, 8)}`
const count = () => props.initial ?? 0 // signal → server-side stub
return (
<div bf-s={...} {...(... ? { "bf-p": __bfPropsJson } : {})}>
<span bf="s1">Count: {bfText("s0")}{count()}{bfTextEnd()}</span>
<button onClick={() => {}} bf="s2">+1</button>
</div>
)
}bf-s— component scope boundary (unique per instance)bf="sN"— client JS targets (elements, text nodes)bfText("s0")/bfTextEnd()— text node markers (rendered as<!--bf:s0-->...<!--/-->)- Signal stubs (
count = () => props.initial ?? 0) — render initial values server-side bf-p— serialized props JSON for client hydration- Event handlers are replaced with no-ops (client JS handles the real ones)
#Script Collection
A build-time post-processing step injects useRequestContext() calls into generated templates. BfScripts renders the collected <script> tags:
import { BfScripts } from '@barefootjs/hono'
export function Layout({ children }) {
return (
<html>
<body>
{children}
<BfScripts />
</body>
</html>
)
}Each component's client JS loads once regardless of instance count. See site/ui/build.ts for the addScriptCollection() pattern.
#Hydration Props
Every client component's props are extended with hydration fields:
| Prop | Purpose |
|---|---|
__instanceId |
Unique instance identifier passed from the parent |
__bfScope |
Parent's scope ID (for nested component communication) |
__bfChild |
Marks this component as a child instance (adds ~ prefix to bf-s value) |
data-key |
Stable key for list-rendered instances |
These are used internally — no manual passing needed.
#Conditional Rendering
Ternaries with element branches use bf-c markers. Text-only ternaries use comment markers:
Element branches:
{loggedIn() ? <span>Welcome back!</span> : <span>Please log in</span>}{loggedIn() ? <span bf-c="s0">Welcome back!</span> : <span bf-c="s0">Please log in</span>}Text-only branches:
{on() ? 'ON' : 'OFF'}{on() ? <>{bfComment("cond-start:s0")}{'ON'}{bfComment("cond-end:s0")}</>
: <>{bfComment("cond-start:s0")}{'OFF'}{bfComment("cond-end:s0")}</>}#Loop Rendering
Source:
{items().map(item => <li>{item}</li>)}Output:
{bfComment('loop')}{items().map((item) => <li>{bfText("s0")}{item}{bfTextEnd()}</li>)}{bfComment('/loop')}Loop markers (<!--bf-loop-->...<!--bf-/loop-->) are used for reconciliation. For child components in loops, the adapter generates unique instance IDs per iteration using the loop index or key.