Vidact Start
Getting started with Vidact Start
Vidact Start adds file-based routing, server rendering, data loaders, and client-side navigation on top of the compiler.
Vidact Start is the full-stack layer for Vidact. It gives you a src/routes directory that maps files to URLs, loaders that run on the server and hand data to your components, HTML rendered on the server and hydrated in the browser, and a <Link> that navigates without a page reload.
It runs on Vite for development and builds to a plain Node server for production. There is nothing to configure about the compiler: Start sets the server and hydrate targets for you.
Create a project
mkdir my-appcd my-apppnpm initpnpm add @vidact/start @vidact/runtime srvxpnpm add -D @vidact/react-types @types/react typescript vitesrvx is a small server adapter used by the production entry; you can substitute any server that accepts a fetch-style handler.
Configure Vite with the Start plugin instead of the bare compiler plugin:
import { vidactStart } from '@vidact/start/vite'import { defineConfig } from 'vite'export default defineConfig({ plugins: [vidactStart()],})Use the same tsconfig.json as the installation guide, adding "node" to types for the server entry.
Add a route
Every file in src/routes is a route. Create the home page:
import { defineFileRoute, type RouteComponentProps } from '@vidact/start'const loader = () => ({ greeting: 'Hello from the server' })export function HomeRoute({ loaderData }: RouteComponentProps<ReturnType<typeof loader>>) { return <h1>{loaderData.greeting}</h1>}export const Route = defineFileRoute({ loader, component: HomeRoute })A route module exports a Route built with defineFileRoute. It can have a component, a loader that runs on the server before rendering, and server.handlers for API endpoints. The loader's return value arrives in the component as loaderData, fully typed.
Add the entries
Start needs three small files: a client entry that hydrates, a server handler that renders, and a production server that listens.
import { hydrateStart } from '@vidact/start/client'import { routeManifest } from 'virtual:vidact-start/routes'await hydrateStart({ manifest: routeManifest })import { createStartHandler } from '@vidact/start/server'import { routeManifest } from 'virtual:vidact-start/routes'export default createStartHandler({ manifest: routeManifest, clientEntry: import.meta.env.DEV ? '/src/client.ts' : '/assets/client.js',})import { fileURLToPath } from 'node:url'import { serve } from 'srvx/node'import { staticMiddleware } from 'srvx/static'import handler from './server.ts'serve({ port: Number(process.env.PORT ?? 4173), middleware: [staticMiddleware({ dir: fileURLToPath(new URL('../client', import.meta.url)) })], fetch: handler,})virtual:vidact-start/routes is generated by the plugin from your src/routes directory. In development, Vite serves src/server.ts directly, so start.ts is only used by the production build.
Run it
pnpm viteOpen the printed URL. The HTML you receive was rendered on the server with the loader's data, and the client entry hydrated it so it is now interactive.
What happens on a request
- The server matches the URL against the route tree.
- Loaders run from the outermost matched route inward. Each sees its parent's data.
- The route components render to HTML on the server.
- The HTML is embedded in a document along with a serialized snapshot of the loader data.
- In the browser,
hydrateStartreads the snapshot, matches the same routes, and attaches to the existing DOM without rebuilding it.
From then on, clicking a <Link> fetches only the snapshot for the new URL and swaps the route content in place.
Next
- Routing covers layouts, dynamic segments, and catch-all routes.
- Data loading covers loaders, parent data, and API handlers.
- Navigation covers
<Link>and programmatic navigation. - Building and deploying covers the production build.