Scaffold a BarefootJS app, run it locally, and tour the generated project. About five minutes.
#Prerequisites
- Node.js 22+ (or Bun, or Deno 2+).
- The default scaffold targets Cloudflare Workers via
wrangler dev— runs locally, no account needed.
#1. Scaffold the project
npm create barefootjs@latestPress Enter at the prompts to accept the defaults (Hono on Cloudflare Workers, UnoCSS).
The CSS prompt has two options:
- UnoCSS (default) — wires up UnoCSS and pulls a starter
<Button>from the BarefootJS UI registry. The rest of this guide follows this path. - None (bring your own CSS) — no UnoCSS, no UI components, no stylesheets. You get just the JSX→template+signals compiler output and a dependency-free starter
Counterbuilt from native<button>elements. Pick this when you want to wire up your own styling. Pass--css noneto select it non-interactively.
#2. Install and run
cd my-app
npm install
npm run devnpm run dev runs three processes in parallel:
bf build --watch— the BarefootJS compiler. Watchescomponents/and emits marked templates plus client JS topublic/components/.unocss --watch— scans your JSX for utility classes and writespublic/uno.css.wrangler dev --live-reload— Cloudflare's local Workers runtime. Serves the app and reloads the browser on rebuilds.
Open the URL Wrangler prints. You should see a counter card with +1, -1, and Reset buttons.
#3. Look at what was generated
my-app/
├── server.tsx # Hono routes — entry point
├── renderer.tsx # HTML shell (<head>, <body>, BfScripts)
├── components/
│ └── Counter.tsx # The starter component
├── components/ui/
│ └── button/ # Pulled from the BarefootJS UI registry
├── public/ # Static assets served by Workers
│ ├── tokens.css # CSS design tokens
│ ├── styles.css # Counter + page styles
│ └── components/ # Generated client JS (bf build writes here)
├── barefoot.config.ts # Compiler + paths config
├── wrangler.jsonc # Cloudflare Workers config
└── uno.config.ts # UnoCSS scan patternsOpen components/Counter.tsx:
'use client'
import { createSignal, createMemo } from '@barefootjs/client'
import { Button } from '@/components/ui/button'
interface CounterProps {
initial?: number
}
export function Counter(props: CounterProps) {
const [count, setCount] = createSignal(props.initial ?? 0)
const doubled = createMemo(() => count() * 2)
return (
<div className="counter">
<p className="counter-value">count: {count()}</p>
<p className="counter-doubled">doubled: {doubled()}</p>
<div className="counter-buttons">
<Button onClick={() => setCount(n => n + 1)}>+1</Button>
<Button onClick={() => setCount(n => n - 1)} variant="secondary">-1</Button>
<Button onClick={() => setCount(0)} variant="ghost">Reset</Button>
</div>
</div>
)
}See Client Directive, createSignal, and createMemo for what each piece does. Props are read via props.initial, not destructured — destructuring captures the value once and breaks reactivity, so the compiler emits warning BF043 when it sees that form on a "use client" component. Props Reactivity covers the full rule.
The Counter is mounted in server.tsx:
import { Hono } from 'hono'
import { renderer } from './renderer'
import { Counter } from '@/components/Counter'
const app = new Hono()
app.use('*', renderer)
app.get('/', (c) =>
c.render(
<main>
<Counter />
</main>,
{ title: 'BarefootJS app' },
),
)
export default appSame Counter import, two outputs: server renders HTML, client hydrates the buttons.
#4. Make a change
With npm run dev still running, pass an initial value from the server. In server.tsx:
app.get('/', (c) =>
c.render(
<main>
<Counter initial={5} />
</main>,
{ title: 'BarefootJS app' },
),
)Save. The browser reloads and the counter starts at 5. The server rendered 5 into the HTML, and hydration picked it up on the client — the same JSX, evaluated in both places.
Now add a new button to Counter.tsx:
<Button onClick={() => setCount(n => n * 2)} variant="ghost">×2</Button>Save and watch the browser update. No virtual DOM, no diff — the compiler generated an effect that updates only the <p> text node when count changes.
#5. Deploy (optional)
When you're ready to ship:
npm run deployThis runs bf build, generates the final uno.css, and calls wrangler deploy. The first deploy will prompt you to log into Cloudflare. After that, your app is live on *.workers.dev.
#Next steps
Agent Skill — Install the BarefootJS skill for Claude Code or Codex and let the agent build components, write IR tests, and debug signals for you.
Core Concepts — the four design principles behind BarefootJS: backend freedom, MPA-style rendering, fine-grained reactivity, and AI-native workflows.
createSignalandcreateMemo— the reactivity primitives you just used.Client Directive — exactly what
"use client"does and when to reach for it.Hono Adapter — adapter-specific configuration and output details.
Pick a different backend by passing
--adapterto the scaffolder. Supported values today:hono(default — Cloudflare Workers),hono-node,echo(Go / Echo),gin(Go / Gin),chi(Go / Chi),nethttp(Go / net/http stdlib),mojo(Mojolicious / Perl),csr(no backend — pure client render). For example:npm create barefootjs@latest -- --adapter hono-nodeSee Adapter Architecture for the architectural overview, and the per-adapter pages under
docs/core/adapters/for output details. The four Go scaffolds (echo,gin,chi,nethttp) all build on the@barefootjs/go-templateadapter, which generates Gohtml/templatefiles; see Go Template Adapter for its programmatic API.