Migrate Vite + Cloudflare Plugin to Void
When to use
Use this skill when the user has an existing Vite app with @cloudflare/vite-plugin and wants to migrate to void with minimal breakage.
Do not use this for greenfield apps started via npm install -D void + npx void init.
Inputs to gather first
Read these files before editing:
package.json
vite.config.*
wrangler.jsonc (if present)
- Current worker entrypoint (
src/worker.*, src/index.*, etc.)
- Any existing API handlers and migration SQL files
Confirm:
- existing routes/endpoints and HTTP methods
- bindings currently used (
DB, KV, R2, etc.)
- whether the app uses framework SSR or only SPA + API
Migration workflow
- Update dependencies
- Remove
@cloudflare/vite-plugin if it is only used for runtime/deploy.
- Add
void.
- Keep existing framework plugins (React/Vue/Svelte/etc.).
- Update Vite config
- Replace
cloudflare(...) plugin usage with voidPlugin().
- Keep plugin order stable unless there is a known conflict.
- Keep unrelated Vite settings unchanged.
Target shape:
import { defineConfig } from 'vite';
import { voidPlugin } from 'void';
export default defineConfig({
plugins: [voidPlugin()],
});
- Migrate API surface to file-based routes
- Create
routes/ if missing.
- Convert each existing endpoint into route files:
routes/api/users.get.ts
routes/api/users.post.ts
routes/api/users/[id].get.ts
- Use
defineHandler from void.
- If there is shared logic, move it into regular modules and import from route files.
- Migrate middleware
- Move request-wide middleware to
middleware/*.ts.
- Export with
defineMiddleware.
- Preserve behavior order by filename prefix when needed (
01.auth.ts, 02.logger.ts).
- Preserve bindings with Void conventions
- Keep Cloudflare-style uppercase names on
c.env (DB, KV, STORAGE, etc.).
- Remove manual binding config from Vite plugin config where Void now infers usage.
- Ensure route code actually references required bindings so inference can detect them.
- Migrations
- Place SQL files in
migrations/ (sorted by filename).
- Keep destructive operations gated by explicit pragma if needed.
- If old migrations live elsewhere, copy/rename into this directory with stable ordering.
- Deploy workflow migration
- Replace old deploy instructions with:
void auth login
void deploy
- If CI must target a specific project, use:
void deploy --project <slug>
- or
VOID_PROJECT=<slug> void deploy
- Post-migration cleanup (remove obsolete Wrangler wiring)
- If the app no longer uses direct Wrangler workflows, remove
wrangler.jsonc.
- Remove direct
wrangler dependency/devDependency from package.json when it is only used for old deploy/dev scripts.
- Remove or rewrite npm scripts that call
wrangler directly (for example old deploy/publish scripts).
- Keep Wrangler only if the project still has explicit non-Void workflows that require it.
Routing behavior reference (use during file conversion)
Apply these filename rules exactly when mapping old handlers to routes/:
- Extension and suffix parsing order
- Strip extension (
.ts, .js, .mts, .mjs).
- Strip env suffix (
.dev, .prod). The suffix restricts the route to that environment, so only use it for handlers that must not ship to the other one.
- Strip HTTP method suffix (
.get, .post, .put, .delete, .patch).
- Strip trailing
index segment.
- Remove route group segments
(group-name) from URL path.
- Method mapping
users.get.ts matches only GET /users.
users.post.ts matches only POST /users.
users.ts matches all methods.
- Split multi-method handlers into one file per method when preserving behavior matters.
- Dynamic and catch-all params
[id] -> :id
[...slug] -> catch-all named param
[...] -> catch-all unnamed fallback
- Use folder nesting for multiple params:
routes/api/org/[org]/repo/[repo].get.ts.
- Route groups and organization
- Directories like
(internal) are for code organization only and do not appear in URL.
- Use them when reorganizing large route sets without changing public paths.
- Ignored files
- Files or directories starting with
_ are ignored by route scanner.
- Do not place active handlers under
_legacy, _draft, etc.
- Middleware behavior
- Middleware files live in
middleware/ and run in filename order.
- Prefix numerically if order is important (
01.auth.ts, 02.logger.ts).
- Concrete mapping examples
src/worker.ts handling GET /api/users/:id -> routes/api/users/[id].get.ts
- single handler switching on method for
/api/users -> routes/api/users.get.ts and routes/api/users.post.ts
- legacy
GET /health endpoint -> routes/health.get.ts
Verification checklist
Run and validate:
npm run dev (or project dev command)
- Exercise representative API routes locally.
npm run build
void deploy and verify live URL responds.
- Confirm no deploy-critical scripts still depend on
wrangler.
In the Void deploy output, verify:
- worker modules uploaded
- static assets uploaded
- migrations applied (if present)
Common migration pitfalls
- Importing from the main
void runtime in worker route files when a lighter handler import is expected by tooling.
- Forgetting to split method-specific handlers (
GET/POST) into filename suffixes.
- Keeping old custom worker entry wiring that conflicts with generated route runtime.
- Binding names changed to lowercase (
db) instead of expected uppercase (DB), breaking inference/provisioning.
Deliverable format
When applying this migration, produce:
- A change summary grouped by config/routes/migrations/CI.
- A list of moved or newly created route files.
- Exact commands to run locally and in CI.
1---2name: migrate-cloudflare-to-void3description: Migrate an existing Vite app using @cloudflare/vite-plugin to Vite + void. Use when a project already runs on Cloudflare Workers but needs Void file-based routes, inferred bindings, and void deploy workflow.4---56# Migrate Vite + Cloudflare Plugin to Void78## When to use910Use this skill when the user has an existing Vite app with `@cloudflare/vite-plugin` and wants to migrate to `void` with minimal breakage.1112Do not use this for greenfield apps started via `npm install -D void` + `npx void init`.1314## Inputs to gather first1516Read these files before editing:1718- `package.json`19- `vite.config.*`20- `wrangler.jsonc` (if present)21- Current worker entrypoint (`src/worker.*`, `src/index.*`, etc.)22- Any existing API handlers and migration SQL files2324Confirm:2526- existing routes/endpoints and HTTP methods27- bindings currently used (`DB`, `KV`, `R2`, etc.)28- whether the app uses framework SSR or only SPA + API2930## Migration workflow31321. Update dependencies3334- Remove `@cloudflare/vite-plugin` if it is only used for runtime/deploy.35- Add `void`.36- Keep existing framework plugins (React/Vue/Svelte/etc.).37382. Update Vite config3940- Replace `cloudflare(...)` plugin usage with `voidPlugin()`.41- Keep plugin order stable unless there is a known conflict.42- Keep unrelated Vite settings unchanged.4344Target shape:4546```ts47import { defineConfig } from 'vite';48import { voidPlugin } from 'void';4950export default defineConfig({51 plugins: [voidPlugin()],52});53```54553. Migrate API surface to file-based routes5657- Create `routes/` if missing.58- Convert each existing endpoint into route files:59 - `routes/api/users.get.ts`60 - `routes/api/users.post.ts`61 - `routes/api/users/[id].get.ts`62- Use `defineHandler` from `void`.63- If there is shared logic, move it into regular modules and import from route files.64654. Migrate middleware6667- Move request-wide middleware to `middleware/*.ts`.68- Export with `defineMiddleware`.69- Preserve behavior order by filename prefix when needed (`01.auth.ts`, `02.logger.ts`).70715. Preserve bindings with Void conventions7273- Keep Cloudflare-style uppercase names on `c.env` (`DB`, `KV`, `STORAGE`, etc.).74- Remove manual binding config from Vite plugin config where Void now infers usage.75- Ensure route code actually references required bindings so inference can detect them.76776. Migrations7879- Place SQL files in `migrations/` (sorted by filename).80- Keep destructive operations gated by explicit pragma if needed.81- If old migrations live elsewhere, copy/rename into this directory with stable ordering.82837. Deploy workflow migration8485- Replace old deploy instructions with:86 - `void auth login`87 - `void deploy`88- If CI must target a specific project, use:89 - `void deploy --project <slug>`90 - or `VOID_PROJECT=<slug> void deploy`91928. Post-migration cleanup (remove obsolete Wrangler wiring)9394- If the app no longer uses direct Wrangler workflows, remove `wrangler.jsonc`.95- Remove direct `wrangler` dependency/devDependency from `package.json` when it is only used for old deploy/dev scripts.96- Remove or rewrite npm scripts that call `wrangler` directly (for example old deploy/publish scripts).97- Keep Wrangler only if the project still has explicit non-Void workflows that require it.9899## Routing behavior reference (use during file conversion)100101Apply these filename rules exactly when mapping old handlers to `routes/`:1021031. Extension and suffix parsing order104105- Strip extension (`.ts`, `.js`, `.mts`, `.mjs`).106- Strip env suffix (`.dev`, `.prod`). The suffix restricts the route to that environment, so only use it for handlers that must not ship to the other one.107- Strip HTTP method suffix (`.get`, `.post`, `.put`, `.delete`, `.patch`).108- Strip trailing `index` segment.109- Remove route group segments `(group-name)` from URL path.1101112. Method mapping112113- `users.get.ts` matches only `GET /users`.114- `users.post.ts` matches only `POST /users`.115- `users.ts` matches all methods.116- Split multi-method handlers into one file per method when preserving behavior matters.1171183. Dynamic and catch-all params119120- `[id]` -> `:id`121- `[...slug]` -> catch-all named param122- `[...]` -> catch-all unnamed fallback123- Use folder nesting for multiple params: `routes/api/org/[org]/repo/[repo].get.ts`.1241254. Route groups and organization126127- Directories like `(internal)` are for code organization only and do not appear in URL.128- Use them when reorganizing large route sets without changing public paths.1291305. Ignored files131132- Files or directories starting with `_` are ignored by route scanner.133- Do not place active handlers under `_legacy`, `_draft`, etc.1341356. Middleware behavior136137- Middleware files live in `middleware/` and run in filename order.138- Prefix numerically if order is important (`01.auth.ts`, `02.logger.ts`).1391407. Concrete mapping examples141142- `src/worker.ts` handling `GET /api/users/:id` -> `routes/api/users/[id].get.ts`143- single handler switching on method for `/api/users` -> `routes/api/users.get.ts` and `routes/api/users.post.ts`144- legacy `GET /health` endpoint -> `routes/health.get.ts`145146## Verification checklist147148Run and validate:1491501. `npm run dev` (or project dev command)1512. Exercise representative API routes locally.1523. `npm run build`1534. `void deploy` and verify live URL responds.1545. Confirm no deploy-critical scripts still depend on `wrangler`.155156In the Void deploy output, verify:157158- worker modules uploaded159- static assets uploaded160- migrations applied (if present)161162## Common migration pitfalls163164- Importing from the main `void` runtime in worker route files when a lighter handler import is expected by tooling.165- Forgetting to split method-specific handlers (`GET`/`POST`) into filename suffixes.166- Keeping old custom worker entry wiring that conflicts with generated route runtime.167- Binding names changed to lowercase (`db`) instead of expected uppercase (`DB`), breaking inference/provisioning.168169## Deliverable format170171When applying this migration, produce:1721731. A change summary grouped by config/routes/migrations/CI.1742. A list of moved or newly created route files.1753. Exact commands to run locally and in CI.