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 expressionsfunction 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 comparatorslocaleCompare 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 */.