Coming from React or Solid

BarefootJS uses Solid's names and shapes for reactivity, React's JSX for control flow, and the compiler for what both do at runtime. Components run once — there is no re-render:

"use client"
import { createSignal, createEffect } from '@barefootjs/client'

export function Counter() {
  const [count, setCount] = createSignal(0)   // useState / createSignal
  createEffect(() => console.log(count()))    // useEffect, no deps array
  return <button onClick={() => setCount(n => n + 1)}>{count()}</button>
}

#Reactive primitives

Solid React BarefootJS
createSignal useState createSignal — [getter, setter], read as count()
createMemo useMemo createMemo — no deps array, auto-tracked
createEffect useEffect createEffect — no deps array; exclude reads with untrack
batch automatic batching batch
untrack deps array untrack — the only way to narrow dependencies
on(deps, fn) deps array — write it with untrack
createStore / produce useReducer / external store — put the object in a signal; forms live in @barefootjs/form
createRoot — createRoot — test and CSR harness use
createRenderEffect / createComputed useLayoutEffect — one effect timing only

#Lifecycle

Solid React BarefootJS
onMount useEffect(fn, []) onMount — once, after hydration
onCleanup effect return value onCleanup
ref={el => …} useRef / ref callback ref={handleMount} — callback ref; the entry point for portals and focus

#Control flow

Solid ships components; BarefootJS lowers React's JSX shapes in the compiler, with Solid's granularity, on every adapter.

Solid React BarefootJS
<Show when fallback> cond && … / ternary cond() && … / ternary
<For each> items.map() items().map() — keyed; .filter() / .sort() / .flatMap() chains allowed
<Switch> / <Match> ternary chain ternary chain
<Index each> — —
<Dynamic component> variable as tag — asChild / Slot cover most uses
<ErrorBoundary> Error Boundary — SSR errors are the backend's; only client effects and handlers can throw
<Portal> createPortal createPortal — imperative: move a ref element to body; SSR leaves an inert placeholder
<Suspense fallback> <Suspense fallback> <Async fallback> — an SSR streaming boundary, compiled to the adapter's primitive
lazy() React.lazy — Vite dynamic import; islands split per entry anyway

#Components and props

Solid React BarefootJS
splitProps destructure + rest splitProps
mergeProps destructure defaults literal destructure defaults, resolved at compile time ({ size = 'md' })
children() props.children props.children + Slot / asChild
createContext createContext createContext
<Ctx.Provider value> same same
useContext useContext useContext
createUniqueId useId — pass props.id in from outside

#Async

Solid React BarefootJS
createResource use(promise) + Suspense createQuery — [value, action]; the server seeds the value from initial, and the component folds "no value yet" as an ordinary conditional instead of suspending
action() / useSubmission useActionState / useFormStatus createMutation — sent when action() is called; action.isPending() / action.error(), invalidates on success. Forms: createForm().isSubmitting()
renderToStream renderToPipeableStream <Async> + setupStreaming()

createQuery and createMutation take a request function that returns an http descriptor, and are Alpha. spec/async.md has the full model; its layers 1 and 2 (a client-side <Async> fold, transitions) are still design.

#Mount and SSR

Solid React BarefootJS
render(() => <App/>, el) createRoot(el).render() render(el, 'Name', props) from @barefootjs/client/runtime
hydrate() hydrateRoot() automatic, per "use client" file
renderToString renderToString — the compiler emits templates; SSR is the backend's job
<NoHydration> Server Components the default: no "use client"
isServer typeof window "use client" / /* @client */ — static, per file or per expression
Solid Devtools React DevTools bf debug graph / bf debug profile

#Router and URL

Solid Router React Router BarefootJS
useSearchParams useSearchParams createSearchParams — signal-shaped; query-only navigation updates it without a page swap
<A href> / useNavigate <Link> / useNavigate plain <a> + queryHref() + router navigate()
— Next layout.tsx (close) <Region> — page lifecycle boundary

#What BarefootJS deliberately does not have

  • Route loaders / render-suspend — the backend fetches what the page needs and passes it as props; a client-side read is a createQuery whose initial is that prop, and nothing suspends.
  • on() / deps arrays — untrack is the one way to narrow what an effect tracks.
  • Stores — a signal holding an object, or @barefootjs/form, covers it.
  • <ErrorBoundary> — SSR errors belong to the backend; only client effects and handlers can throw.
  • renderToString / hydrate() — templates render on the server; hydration is automatic per "use client" file.
  • createUniqueId — ids come in as props.
  • useTransition / <ViewTransition> — the router only sets data-bf-navigating.