| name | mandu-fs-routes |
| description | 파일 시스템 라우팅 규칙. layout에 html/head/body 금지.
app/ 폴더, page.tsx, route.ts, layout.tsx, [id] 작업 시 자동 호출.
|
Mandu FS Routes
파일 시스템 기반 라우팅. app/ 폴더의 파일 구조가 URL이 됩니다.
File Naming Conventions
| File | Purpose | Export |
|---|
page.tsx | Page component | default function component |
route.ts | API handler | default Mandu.filling() chain |
layout.tsx | Shared layout | default function with children prop |
loading.tsx | Loading UI | default function component |
error.tsx | Error UI | default function component |
URL Mapping
| File Path | URL |
|---|
app/page.tsx | / |
app/about/page.tsx | /about |
app/blog/[slug]/page.tsx | /blog/:slug |
app/users/[id]/page.tsx | /users/:id |
app/docs/[...path]/page.tsx | /docs/* (catch-all) |
app/(auth)/login/page.tsx | /login (grouped) |
app/api/users/route.ts | /api/users |
app/api/users/[id]/route.ts | /api/users/:id |
Layout Rules (CRITICAL)
Layout MUST NOT include <html>, <head>, or <body> tags.
Mandu SSR generates these automatically.
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<div className="min-h-screen bg-background font-sans antialiased">
{children}
</div>
);
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html> // <-- DO NOT include
<head /> // <-- DO NOT include
<body> // <-- DO NOT include
{children}
</body>
</html>
);
}
Nested layouts wrap child pages:
app/layout.tsx -> wraps ALL pages
app/dashboard/layout.tsx -> wraps /dashboard/* pages
Page Components
export default function DashboardPage() {
return (
<main>
<h1>Dashboard</h1>
</main>
);
}
Pages are server-rendered by default. No "use client" needed unless using Islands.
API Routes
import { Mandu } from "@mandujs/core";
export default Mandu.filling()
.get((ctx) => ctx.ok({ users: [] }))
.post(async (ctx) => {
const body = await ctx.body<{ name: string }>();
return ctx.created({ user: body });
});
API routes use .ts extension (NOT .tsx).
Dynamic Routes
export default function UserPage({ params }: { params: { id: string } }) {
return <h1>User {params.id}</h1>;
}
export default function DocsPage({ params }: { params: { path: string[] } }) {
return <h1>Doc: {params.path.join("/")}</h1>;
}
Route Groups
Parenthesized folders create logical groups without affecting the URL:
app/(marketing)/about/page.tsx -> /about
app/(marketing)/pricing/page.tsx -> /pricing
app/(app)/dashboard/page.tsx -> /dashboard
Common Mistakes
- Adding
<html>/<head>/<body> in layout.tsx (SSR does this)
- Using
route.tsx instead of route.ts for API handlers
- Forgetting to export default from page/layout files
- Mixing page.tsx and route.ts in the same directory
See also
mandu-mcp-create-flow — 라우트 추가 시 mandu_add_route + contract + generate 순서