Skip to content
Built with

Project Structure ​

Overview ​

A Void app uses file-based conventions to define routes, pages, middleware, and more. All directories are optional, so you only need the pieces your app actually uses.

routes/ ​

File-based HTTP API endpoints built on Hono. Each file exports named HTTP method handlers.

  • routes/
    • api/
      • hello.ts→ GET|POST /api/hello
      • users/
        • index.ts→ GET|POST /api/users
        • [id].ts→ GET|PUT|DELETE /api/users/:id
      • search/
        • [...query].ts→ GET /api/search/* (catch-all)
  • Named exports: export const GET = defineHandler(...), export const POST = ...
  • Dynamic segments: [id] becomes :id route parameter
  • Catch-all: [...slug] matches the remaining path
  • Route groups: (admin)/ organizes files without affecting URL paths
  • Files starting with _ are ignored
  • Environment suffix: debug.dev.ts builds in development only, metrics.prod.ts in production only

See Server Routing for the full guide.

pages/ ​

Full-stack server-rendered pages with co-located data loading. Available with Vue, React, Svelte, and Solid adapters.

  • pages/
    • layout.tsxRoot layout (wraps all pages)
    • index.tsx→ /
    • index.server.tsServer-side loader & action for /
    • about.tsx→ /about
    • users/
      • layout.tsxNested layout for /users/*
      • [id].tsx→ /users/:id
      • [id].server.tsServer-side loader & action for /users/:id
    • blog/
      • hello.md→ /blog/hello (Markdown page)
  • .server.ts files export loader for GET data and action for mutations, paired with the page component of the same name
  • Layouts nest automatically and persist across navigations
  • Markdown pages are supported with frontmatter

See Pages Routing for the full guide.

middleware/ ​

Global middleware that runs on every request. Numeric prefixes control execution order.

  • middleware/
    • 01.logger.tsRuns first
    • 02.auth.tsRuns second

Each file default-exports a defineMiddleware() handler. Use middleware to set shared context (auth, logging, rate limiting) available to all routes.

db/ ​

Drizzle schemas and SQL migrations for D1, PostgreSQL, or MySQL.

  • db/
    • schema.tsDrizzle schema (source of truth)
    • schema/Optional: split into multiple files
    • migrations/
      • 20260410161500_*.sql
      • 20260410161501_*.sql
  • db/schema.ts: your Drizzle table definitions and the source of truth for DB types
  • db/schema/: optional. Split tables into separate files and re-export them from db/schema.ts
  • db/migrations/: generated SQL migration files, applied in order on deploy

Generate schema with void gen model or write it by hand. Generate migrations with void db generate.

crons/ ​

Scheduled jobs. Each file exports a cron expression and a default defineScheduled() handler.

queues/ ​

Queue consumers. Each file default-exports a defineQueue() handler.

email/ ​

Inbound email handlers. A file's name selects the recipient; _default.ts handles other addresses.

durable-objects/ ​

Durable State modules. Each file default-exports a defineDurableState() definition.

src/ ​

Shared application code such as components, utilities, and types. This directory has no special convention, so organize it however you like.

public/ ​

Static assets served as-is at the root path. Files here are not processed by Vite.

.void/ ​

Generated types and local development state (gitignored). Run void prepare to create the types, or let vite dev and vite build update them.

Config Files ​

FilePurpose
vite.config.tsVite configuration. Must include voidPlugin().
void.config.tsOptional Void config for routing, inference, and worker settings
tsconfig.jsonTypeScript config. Extend .void/tsconfig.json for auto-generated types; run void init --tsconfig when an existing config already uses extends.
.envLocal development values only (gitignored; never deployed)
env.tsChecked-in env names, types, defaults, and requiredness

See Environment Variables for local, production-secret, and client build behavior.