Router

Practices

Waku provides a router built on top of the minimal API, and it serves as a reference implementation. While other router implementations can be used with Waku, this page focuses on the waku/router implementation.

Client API

To use the router, it is required to use the Router component instead of using serve directly. The following code demonstrates how to use the Router component as the root component:

import { createRoot } from "react-dom/client";
import { Router } from "waku/router/client";

const root = createRoot(document.getElementById("root")!);

root.render(<Router />);

The Router component internally uses serve and handles nested routes.

Server API

In entries.ts, we use defineRouter to export getEntry and getBuildConfig at once. Here's a simple example code without builder:

import { defineRouter } from "waku/router/server";

export default defineRouter(
(id) => {
switch (id) {
case 'index':
return import('./routes/index.tsx');
case 'foo':
return import('./routes/foo.tsx');
default:
throw new Error("no such route");
}
}
);

The implementation of the defineRouter is config-based. However, it isn't too difficult to make a file-based router. Here's a file-based example code with builder:

import url from "node:url";
import path from "node:path";

import { glob } from "glob";
import { defineRouter } from "waku/router/server";

const routesDir = path.join(
path.dirname(url.fileURLToPath(import.meta.url)),
"routes",
);

export default defineRouter(
// getComponent (id is "**/layout" or "**/page")
async (id) => {
const files = await glob(`${id}.{tsx,js}`, { cwd: routesDir });
if (files.length === 0) {
return null;
}
const items = id.split("/");
switch (items.length) {
case 1:
return import(`./routes/${items[0]}.tsx`);
case 2:
return import(`./routes/${items[0]}/${items[1]}.tsx`);
case 3:
return import(`./routes/${items[0]}/${items[1]}/${items[2]}.tsx`);
default:
throw new Error("too deep route");
}
},
// getAllPaths
async () => {
const files = await glob("**/page.{tsx,js}", { cwd: routesDir });
return files.map(
(file) => "/" + file.slice(0, Math.max(0, file.lastIndexOf("/"))),
);
},
);

Due to the limitation of bundler, we cannot automatically allow infinite depth of routes.

How to try it

You can try an example app in the repository by cloning it and running the following commands:

git clone https://github.com/dai-shi/waku.git
cd waku
npm install
npm run examples:dev:07_router

Alternatively, you could create a project with something like npm create waku@latest and copy files from the example folder in the repository.