Front ends

One command. One origin. One deploy.

A React 19 front end goes inside a Worker you already have, wired end to end. The SPA and the API build together, deploy together, and answer on one origin — with the screens you were going to write already written.

terminal
pithy ui add react --worker api

One command, and the wiring is done.

It scaffolds the client into the Worker's own directory and edits three files to connect them: an assets stanza in wrangler.jsonc with SPA fallback and an allowlist of the API paths the Worker answers itself, a dev block so pithy dev runs Vite for this Worker, and a ui block so pithy deploy builds the client before shipping.

Your Worker entry is untouched, before and after.

terminal
$ pithy ui add react --worker api
▸ client     apps/api/src
▸ wrangler   assets + SPA fallback
▸ dev        vite, one process
▸ deploy     builds before shipping
Done.

The screens you were going to write are already there.

What you get depends on what the Worker already composes — that is the composability doing the work rather than a template menu.

Compose auth and you get the sign-in screens: magic link, the OTP entry, and the callback route. Compose payments and you get the paywall, the pricing screen and the subscription screen. Compose neither and you get a router and one screen of your own.

apps/api/src/routes
pithy/sign-in.tsx        auth      magic link
pithy/otp.tsx            auth      code entry
pithy/callback.tsx       auth      the return leg
pithy/paywall.tsx        payments  gated content
pithy/pricing.tsx        payments  plans, priced live
pithy/subscription.tsx   payments  manage, cancel
index.tsx                          yours

The client knows what your backend can do.

No generated SDK, no types file to regenerate and commit. What the front end knows about each capability comes from virtual modules resolved in memory at build time, so it cannot drift from the Worker it ships beside.

Add a capability and its client projection appears. Remove one and the import stops typechecking, which is the point.

myapp/apps/api/src/pithy-config.tsx
import { auth } from "virtual:pithy/auth";
import { payments } from "virtual:pithy/payments";

// Narrowed to exactly what this Worker composes —
// resolved in memory, never written to disk.
export const pithy = { auth, payments };

Every line of it is yours to delete.

The scaffold is a starting point, not a dependency. Rewrite styles.css from scratch, delete a Pithy screen and write your own, change the router. Nothing upstream will argue with you and nothing silently reverts.

It does not overwrite anything, ever — running it twice will not clobber the edits you made.

terminal
$ rm apps/api/src/routes/pithy/sign-in.tsx
$ # write your own; nothing upstream objects

$ pithy dev
▸ vite     one process, one origin
▸ api      same Worker, same port
Done.

What it does not do

A tool that says no is easier to trust.

It does not create a Worker.

It scaffolds into one that exists. pithy worker add makes new ones.

It does not touch your entry.

Your Worker's index.ts is identical before and after.

It does not overwrite.

Ever. Run it twice and your edits survive.

It writes no build artifacts.

No generated SDK or types file on disk — virtual modules, resolved in memory.

It takes no provider flags.

Which social providers you offer lives in pithy.config.ts and nowhere else.

It does not scaffold mobile.

Bearer auth is fully supported and documented; this stub is simply not that.

One command puts a front end in your Worker.

Sign-in, paywall, pricing and subscription screens, scaffolded from what you already compose.

Get started Building alone?