#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@latest

Press Enter at the prompts to accept the defaults (Hono on Cloudflare Workers, UnoCSS). The CSS prompt also offers None, which skips UnoCSS and the UI registry and starts you with a dependency-free Counter; pass --css none to pick it non-interactively.

#2. Install and run

cd my-app
npm install
npm run dev

npm run dev runs the compiler (as a Vite plugin), the UnoCSS watcher, and wrangler dev in parallel. 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 (vite build writes here)
├── vite.config.ts          # Compiler (barefoot plugin) + Vite build config
├── wrangler.jsonc          # Cloudflare Workers config
└── uno.config.ts           # UnoCSS scan patterns

Open 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>
  )
}

"use client" marks the component as interactive (Component Authoring); createSignal holds the count and createMemo derives from it. Reading props.initial or destructuring { initial } in the parameter compiles to the same live read (Props Reactivity).

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 app

Same import, two outputs: the server renders HTML, the 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 it into the HTML and hydration picked it up.

Then add a button to Counter.tsx:

<Button onClick={() => setCount(n => n * 2)} variant="ghost">×2</Button>

Save and watch the browser update. The compiler generated an effect that updates only the <p> text node when count changes.

#5. Deploy (optional)

npm run deploy

This runs vite build, generates the final uno.css, and calls wrangler deploy. The first deploy prompts you to log into Cloudflare; after that the app is live on *.workers.dev.

#Next steps

  • Agent Skill — let Claude Code or Codex build components, write IR tests, and debug signals.
  • How It Works — what the compiler emits and how hydration attaches to it.
  • Components — authoring, children, context, portals.
  • Reactivity — signals, memos, effects.
  • Adapters — --adapter picks a backend: hono (default), hono-node, echo, gin, chi, nethttp, mojo, csr.