Skip to main content

Vite Plugin

@exodra/vite-plugin wires Exodra into Vite: it compiles Exodra JSX with the Babel plugin, adds Hot Module Replacement for components, and generates file-based routes and API endpoints from your directory structure.

Installation​

npm install --save-dev @exodra/vite-plugin

Configuration​

Add the plugin to your Vite config. It runs with enforce: 'pre' so it owns the JSX pipeline before Vite's esbuild, and it sets esbuild's jsx: 'preserve' so no foreign JSX runtime is injected.

// vite.config.ts
import { defineConfig } from 'vite';
import exodra from '@exodra/vite-plugin';

export default defineConfig({
plugins: [
exodra({
pagesDir: 'src/pages', // Default: src/pages
apiDir: 'src/api', // Default: src/api
ssr: false, // Default: false
alias: '@', // Default: @
}),
],
});

Options​

OptionTypeDefaultDescription
pagesDirstring'src/pages'Directory for page components.
apiDirstring'src/api'Directory for API routes.
ssrbooleanfalseEnable SSR support.
aliasstring'@'Import alias.

JSX transform​

The plugin transforms every .tsx / .jsx file (outside node_modules) through @exodra/babel-plugin-jsx, compiling Exodra JSX to h() calls. Because it runs before esbuild, the output is plain JS by the time Vite sees it. See the Babel Plugin page for the bucket rules.

File-based routing​

Routes are generated automatically from your pagesDir:

src/pages/
├── index.tsx → /
├── about.tsx → /about
├── _layout.tsx → Layout wrapper
├── _error.tsx → Error boundary
├── blog/
│ ├── index.tsx → /blog
│ ├── [id].tsx → /blog/:id
│ ├── [...slug].tsx → /blog/* (catch-all)
│ └── _layout.tsx → Blog layout
├── shop/
│ ├── [[...path]].tsx → /shop (optional catch-all)
│ └── checkout.tsx → /shop/checkout
└── admin/
├── _layout.tsx → Admin layout
└── dashboard.tsx → /admin/dashboard

Special files​

  • _layout.tsx — wraps all pages in the directory (inherited by nested dirs).
  • _error.tsx — error boundary for the directory.
  • index.tsx — default route for a directory.

Dynamic routes​

  • [param].tsx — single dynamic segment (:param).
  • [...params].tsx — catch-all route.
  • [[...params]].tsx — optional catch-all route.

Using generated routes​

Import the routes from the virtual:exodra-routes module (route components are lazily imported):

// main.tsx
import { mount } from '@exodra/dom';
import {
createRouter,
createBrowserHistory,
RouterProvider,
Outlet,
} from '@exodra/router';
import { routes } from 'virtual:exodra-routes';

const router = createRouter(routes, { history: createBrowserHistory() });

mount(
<RouterProvider static={{ router }}>
<Outlet />
</RouterProvider>,
document.getElementById('app')!,
);

For TypeScript, declare the virtual module:

// env.d.ts
/// <reference types="vite/client" />

declare module 'virtual:exodra-routes' {
import type { TExoRoute } from '@exodra/router';
export const routes: TExoRoute[];
}

API routes​

Files in apiDir become API endpoints. Export functions named after the HTTP method; they are served during development at /api/<file>:

// src/api/users.ts
export async function GET(request: Request) {
const users = await fetchUsers();
return new Response(JSON.stringify(users), {
headers: { 'Content-Type': 'application/json' },
});
}

export async function POST(request: Request) {
const data = await request.json();
const user = await createUser(data);
return new Response(JSON.stringify(user), {
status: 201,
headers: { 'Content-Type': 'application/json' },
});
}

If no matching method (or default) export is found, the request returns 405 Method not allowed.

Hot Module Replacement​

The plugin adds HMR glue to components and emits custom events when files change:

  • exodra:component-update — a component was updated (with preserveState).
  • exodra:route-update — the route manifest changed.

For HMR to work, ensure components are default exports and Vite HMR is enabled.

SSR​

Enable ssr: true and mark Exodra packages as non-external for the SSR build:

// vite.config.ts
export default defineConfig({
plugins: [exodra({ ssr: true })],
ssr: {
noExternal: ['@exodra/core', '@exodra/ssr'],
},
});

See the SSR guide for server rendering and hydration.