Routing
RebaseJS uses a file-system router. Every page.tsx in the app/ directory becomes a route. No config, no manual registration.
Basic Routes
| File | Route |
|---|---|
app/page.tsx | / |
app/about/page.tsx | /about |
app/posts/page.tsx | /posts |
app/posts/[id]/page.tsx | /posts/:id |
app/posts/[id]/edit/page.tsx | /posts/:id/edit |
app/[...slug]/page.tsx | /anything/deeply/nested |
Layouts
Place a layout.tsx in any directory. RebaseJS automatically wraps child pages with the nearest parent layouts, outermost first.
// app/layout.tsx — wraps every page
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className="bg-white text-gray-900">
{children}
</body>
</html>
);
}// app/dashboard/layout.tsx — wraps /dashboard/* only
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1 p-8">{children}</main>
</div>
);
}Dynamic Segments
Use [param] folder names for dynamic routes. Params are passed to the page as props.
// app/posts/[id]/page.tsx
export default function PostPage({ params }: { params: { id: string } }) {
return <h1>Post {params.id}</h1>;
}Loading UI
Create loading.tsx in any directory to show a Suspense fallback while data loads.
// app/posts/[id]/loading.tsx
export default function Loading() {
return <div className="animate-pulse h-8 bg-gray-200 rounded" />;
}Error Boundaries
Create error.tsx to catch render errors in a subtree.
// app/posts/[id]/error.tsx
export default function PostError({ error, reset }: { error: Error; reset: () => void }) {
return (
<div className="p-8 text-center">
<p className="text-red-600">{error.message}</p>
<button onClick={reset} className="mt-4 btn">Try again</button>
</div>
);
}Not-Found Page
// app/not-found.tsx
export default function NotFound() {
return (
<div className="p-8 text-center">
<h1 className="text-4xl font-bold">404</h1>
<p className="mt-2 text-gray-600">Page not found.</p>
</div>
);
}API Routes
Create route.ts in any directory to expose HTTP endpoints.
// app/api/posts/route.ts
export async function GET(req: Request): Promise<Response> {
const posts = await db.posts.findAll();
return Response.json(posts);
}
export async function POST(req: Request): Promise<Response> {
const body = await req.json();
const post = await db.posts.create(body);
return Response.json(post, { status: 201 });
}Supported methods: GET, POST, PUT, PATCH, DELETE, HEAD.
Middleware
Create middleware.ts at the project root to run code before every request.
// middleware.ts
import { redirect, next } from "rebasejs/middleware";
export async function middleware(req: Request) {
const { pathname } = new URL(req.url);
if (pathname.startsWith("/dashboard")) {
const session = req.headers.get("cookie")?.includes("session");
if (!session) return redirect("/login");
}
return next();
}
// Optional: restrict to specific paths
export const config = {
matcher: ["/dashboard/:path*", "/api/:path*"],
};Type-Safe Navigation
RebaseJS generates a RebaseRoutes TypeScript union from the route manifest at build time. Add .rebasejs/routes.d.ts to your tsconfig.json to enable compile-time checks on every link.
// tsconfig.json
{
"include": ["app", ".rebasejs/routes.d.ts"]
}import { RouteLink } from "rebasejs/components";
import { navigate } from "rebasejs/router";
// ✅ known static path
<RouteLink to="/about">About</RouteLink>
// ✅ known dynamic path — template literal
<RouteLink to={`/posts/${post.id}`}>Read post</RouteLink>
// ✗ build error: "/abuot" is not assignable to RebaseRoutes
<RouteLink to="/abuot">Typo</RouteLink>The Rust route checker also walks all .tsx/.ts source files and validates every <RouteLink to>, <Link href>, and navigate() string literal against the manifest. Unknown paths fail the build with the file path, character offset, and a close-match suggestion.
i18n Routing
// i18n.ts
import { createI18nConfig } from "rebasejs/i18n";
export const i18n = createI18nConfig({
locales: ["en", "fil", "es"],
defaultLocale: "en",
});// middleware.ts
import { i18n } from "./i18n.js";
import { redirect, next } from "rebasejs/middleware";
export async function middleware(req: Request) {
const { pathname } = new URL(req.url);
if (!i18n.hasLocalePrefix(pathname)) {
return redirect(`/${i18n.detectLocale(req)}${pathname}`);
}
return next();
}// app/[locale]/layout.tsx
import { LocaleProvider } from "rebasejs/i18n";
export default function LocaleLayout({ params, children }) {
return <LocaleProvider locale={params.locale}>{children}</LocaleProvider>;
}