Instruction file imported from stephenlautier/rift (
.github/instructions/vike.instructions.md). Copyright stays with the author.
Vike Conventions
File-Based Routing
Each page is a directory under pages/ with +Page.tsx:
pages/
index/
+Page.tsx # route: /
+data.ts # data fetching
+title.ts # <title> tag
champions/
+Page.tsx # route: /champions
+data.ts
@id/
+Page.tsx # route: /champions/:id
+data.ts
+route.ts # optional custom route
tier-list/
+Page.tsx # route: /tier-list
+data.ts
+Layout.tsx # root layout (wraps all pages)
+config.ts # root config (extends vike-react)
Config (+config.ts)
Always extend vike-react in the root config:
// pages/+config.ts
import vikeReact from "vike-react/config";
export default {
extends: [vikeReact],
ssr: true,
};
Per-page overrides (e.g. disable SSR for a page):
// pages/some-spa-page/+config.ts
export default { ssr: false };
Data Fetching (+data.ts)
// pages/champions/@id/+data.ts
import type { PageContextServer } from "vike/types";
import { getChampion } from "@rift/data-access";
export async function data(pageContext: PageContextServer) {
const { id } = pageContext.routeParams;
return getChampion(id); // return value becomes `pageContext.data`
}
export type Data = Awaited<ReturnType<typeof data>>;
Consume with useData() in the component:
import { useData } from "vike-react/useData";
import type { Data } from "./+data";
export default function Page() {
const champion = useData<Data>();
return <lol-champion-card champion={champion} />;
}
Head Tags (+Head.tsx)
// pages/champions/@id/+Head.tsx
import { useData } from "vike-react/useData";
import type { Data } from "./+data";
export default function Head() {
const { name } = useData<Data>();
return <title>{name} — LoL Champions</title>;
}
API Routes via Hono (+server.ts)
Hono server handler lives alongside pages or in a dedicated server/ dir:
// server/index.ts
import { Hono } from "hono";
import { championsRouter } from "./routers/champions";
const app = new Hono();
app.route("/api/champions", championsRouter);
export default app;
Guards (auth protection)
// pages/protected/+guard.ts
import type { PageContextServer } from "vike/types";
import { render } from "vike/abort";
export async function guard(pageContext: PageContextServer) {
if (!pageContext.user) throw render(401);
}
Common Pitfalls
+data.tsruns server-side only — never import browser APIs there- Use
.server.tssuffix for files that must never be bundled for the client - Layouts at
pages/+Layout.tsxwrap ALL pages — use nested+Layout.tsxfor sub-trees pageContext.datais serialized to JSON — keep it plain; no class instances- Use
@/for intra-app imports (maps to the app's ownsrc/); never use relative../../../for cross-lib imports - Use
@rift/champion,@rift/player,@rift/data-accessfor cross-lib imports - Follow vertical slice architecture within
src/— group by feature (e.g.src/champion/), not by type (src/components/)