Skip to content

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 boundary

Every non-underscore file under src/pages is a route. The path maps to the URL:

FileRoute
pages/index.tsx/
pages/about.tsx/about
pages/users/[id].tsx/users/:id

Files prefixed with _ are conventions, not routes:

  • _layout.tsx wraps every page in its directory (and subdirectories). Use it for shared chrome — nav, footer, a devtools panel.
  • _loading.tsx renders while an async route is resolving.
  • _error.tsx is 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.

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.

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.