Project structure
A scaffolded Reactra app looks like this:
my-app/├── index.html├── vite.config.ts├── tsconfig.json├── package.json└── src/ ├── main.tsx # boots the router, mounts <RouteRenderer> ├── vite-env.d.ts # ambient types (CSS modules, vite/client) ├── routeManifest.generated.ts # generated — do not edit └── pages/ ├── index.tsx # → / ├── _layout.tsx # wraps every page in this folder ├── _loading.tsx # shown while an async route resolves └── _error.tsx # route-level error boundarysrc/pages/** — file-based routes
Section titled “src/pages/** — file-based routes”Every non-underscore file under src/pages is a route. The path maps to the URL:
| File | Route |
|---|---|
pages/index.tsx | / |
pages/about.tsx | /about |
pages/users/[id].tsx | /users/:id |
Files prefixed with _ are conventions, not routes:
_layout.tsxwraps every page in its directory (and subdirectories). Use it for shared chrome — nav, footer, a devtools panel._loading.tsxrenders while an async route is resolving._error.tsxis the error boundary for routes in its directory.
routeManifest.generated.ts — generated, don’t edit
Section titled “routeManifest.generated.ts — generated, don’t edit”@reactra/vite-plugin walks src/pages/** and writes routeManifest.generated.ts,
exporting the ROUTES array that main.tsx hands to configureRouter. It
regenerates on dev start and whenever a page file changes.
It’s git-ignored, but the scaffold ships a committed placeholder so a fresh clone
type-checks before the first vite dev run. Don’t import from it by hand beyond
main.tsx.
main.tsx — the entry point
Section titled “main.tsx — the entry point”import { StrictMode } from "react"import { createRoot } from "react-dom/client"import { configureRouter, RouteRenderer } from "@reactra/router"import { ROUTES } from "./routeManifest.generated"
configureRouter({ mode: "history", routes: ROUTES })
createRoot(document.getElementById("root")!).render( <StrictMode> <RouteRenderer /> </StrictMode>,)If your app uses stores or services, the generated manifest also exports STORES /
SERVICES, and main.tsx passes them to configureStores / configureServices.
vite.config.ts — plugin order matters
Section titled “vite.config.ts — plugin order matters”import { defineConfig } from "vite"import react from "@vitejs/plugin-react"import reactra from "@reactra/vite-plugin"
export default defineConfig({ plugins: [reactra(), react()],})reactra() must come first — it runs at enforce: "pre" and transforms Reactra DSL
into React-19 TSX, which @vitejs/plugin-react then transforms into JS.