Adopt an existing Vite app

You already have a Vite app. You would rather not start over.

First: decide whether you need to

A new app should just run pithy ui add. The shape you are moving toward is one origin, one deploy, and front ends overview is the map.

You do not have to adopt anything. A front end deployed elsewhere is an HTTP client like any other, and the kit is happy to serve it.

What you give up by leaving it where it is:

Cookie sessions. Cross-origin means the bearer path instead — fully supported, and more work in your client.

No CORS. You will be configuring it.

One deploy. Two artifacts, two versions, and the possibility of them being out of step.

The one thing you must get right either way is your trusted origins list, so CSRF and OAuth redirects work.

The shape you are moving to

Everything lives in the Worker’s own directory. The entry document, the build config, the client source, the client’s type configs.

The Worker’s entry file is untouched, before and after.

What you have to do by hand

Move the app into the Worker’s directory. Source, entry document, assets.

Merge the build config. Yours plus the Cloudflare plugin and the Pithy plugin. This is the fiddly part, and the two things that are not optional:

The strict-port flag, so the dev server fails loudly rather than drifting off a busy port — a Worker that quietly moves breaks every sibling that was told its address.

The config-loader flag on both the dev and build commands. It is load-bearing rather than a preference: the default loader bundles your config and leaves the plugin external, which asks the runtime to import raw TypeScript with extensionless relative imports — it cannot resolve those, and refuses to strip types under the dependency directory at all.

Add the two type configs, one for the client and one for the build config, and reference them from the root solution file — a program nothing references is a program nothing checks.

Write the asset stanza. The SPA fallback, and the worker-first allowlist. Do not hand-write the allowlist:

pithy ui sync --worker api

That derives it from the real route table, which is the one part you genuinely should not do yourself.

Write the worker manifest blocks — the dev command with its port token, and the build command.

Add the one-React rule to both files that resolve modules. No command will put it there for you, and until it is there an invalid-hook-call error is exactly where it was.

The rules your app now has to follow

Every client file is .tsx. The Worker’s type config includes plain TypeScript files, which does not match the JSX extension — so the Worker’s program ignores the client entirely. A .ts client file breaks that.

No Worker file may import a client file. The seam is runtime-only, and one import across it pulls the browser build into the Worker’s type program.

The ambient declarations file sits at the Worker root, not under the source directory — where it would match the Worker’s includes.

What you keep

Your router, if you have one. The scaffolded one is a convenience rather than a requirement.

Your component library, your styling, your state management, your data fetching. None of it is touched.

Your build output shape, mostly — the plugin owns the asset directory setting, which is why the scaffold deliberately does not write it: it overwrites the key silently rather than erroring, so a value there would be a lie in your own config.

What you gain, and when it starts working

One origin the moment the asset stanza is right.

Cookie sessions the moment you stop sending bearer tokens.

One deploy the moment the build block is in the manifest.

Each is independent, so you can move in stages rather than all at once.

The honest limits

The scaffolded screens are not available to you unless you also scaffold, which you cannot do into a Worker that already has a UI block. You are writing your own sign-in flow against the same routes — which is a documented path rather than a hard one.

Nothing backfills. Every file the scaffold would have written is one you are writing yourself, and a future release that adds a stub file will not add it to a project that never had the block.

You own the merge. The build config in particular has several load-bearing details, and getting one wrong fails in a way that points at the wrong thing.

A middle path worth considering

Scaffold into a new Worker, look at what it wrote, and then move your components into it.

pithy worker add web costs a directory. You get every file correct by construction, and the migration becomes move my components rather than reconstruct the wiring — which is the half that is actually hard.

ESC