BarefootJS uses standard JSX syntax. If you have written React or SolidJS components, most patterns work as you expect.
#Control Flow
Standard JavaScript control flow in JSX works:
// Ternary
{count() > 0 ? <p>{count()} items</p> : <p>No items</p>}
// Logical AND
{isLoggedIn() && <Dashboard />}
// Conditional return
if (status === 'empty') {
return <p>No items yet.</p>
}
return <div>...</div>#List Rendering
.map() renders lists:
{todos().map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}.filter().map() chains work when the predicate uses supported expressions — simple single expressions and block bodies with variable declarations, if/return statements:
// ✅ Simple predicate
{todos().filter(t => !t.done).map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
// ✅ Block body with simple statements — also works
{todos().filter(t => {
const f = filter()
if (f === 'active') return !t.done
if (f === 'completed') return t.done
return true
}).map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}.sort() and .toSorted() can be chained with .map() and .filter():
// ✅ Sort then render
{items().sort((a, b) => a.price - b.price).map(item => (
<Item key={item.id} item={item} />
))}
// ✅ Filter, sort, then render
{items().filter(x => x.active).sort((a, b) => a.name > b.name ? 1 : -1).map(item => (
<Item key={item.id} item={item} />
))}Some comparators (e.g., localeCompare, block bodies) are not supported and will produce a compile error — use /* @client */ in that case.
#Event Handling
on* attributes bind event handlers. The handler receives the native DOM event:
<button onClick={() => setCount(n => n + 1)}>+1</button>
<input onInput={(e) => setText((e.target as HTMLInputElement).value)} />
<input onKeyDown={(e) => e.key === 'Enter' && handleSubmit()} />#Dynamic Attributes
Expressions in attributes are reactive:
<button disabled={!accepted()}>Submit</button>
<a className={filter() === 'all' ? 'selected' : ''}>All</a>
<div style={`background: ${accepted() ? '#4caf50' : '#ccc'}`}>...</div>#Limitations
BarefootJS compiles JSX into server templates (Go html/template, Hono JSX, etc.) and client JS. Some JavaScript expressions cannot be translated into server template syntax.
When the compiler encounters an unsupported expression, it emits a compile error (BF021). Add /* @client */ to explicitly opt into client-only evaluation for these expressions.
#Unsupported patterns
Nested higher-order methods — a higher-order method inside a predicate of another:
// ❌ Compile error (BF021)
{items().filter(x => x.tags().filter(t => t.active).length > 0)}
// ✅ Add /* @client */ to evaluate on the client
{/* @client */ items().filter(x => x.tags().filter(t => t.active).length > 0)}Unsupported array methods — .reduce(), .forEach(), .flatMap() and others cannot be translated to server template syntax:
// ❌ Compile error (BF021)
{items().reduce((sum, x) => sum + x.price, 0)}
// ✅ Use /* @client */
{/* @client */ items().reduce((sum, x) => sum + x.price, 0)}Destructuring in predicate parameters — the compiler requires a single named parameter:
// ❌ Compile error (BF021)
{items().filter(({done}) => done).map(...)}
// ✅ Use a named parameter instead
{items().filter(t => t.done).map(...)}Function expressions — function keyword syntax is not supported:
// ❌ Compile error (BF021)
{items().filter(function(x) { return x.done })}
// ✅ Use arrow functions instead
{items().filter(x => x.done)}Unsupported sort comparators — localeCompare and block body comparators cannot be compiled:
// ❌ Compile error (BF021)
{items().sort((a, b) => a.name.localeCompare(b.name)).map(...)}
// ✅ Use /* @client */
{/* @client */ items().sort((a, b) => a.name.localeCompare(b.name)).map(...)}See the TodoApp example for a real-world component using /* @client */.