Routing
Cloudwerk uses a file-based routing system where your file structure defines your application’s routes. This guide covers everything from basic routes to advanced patterns.
How Routing Works
Section titled “How Routing Works”Cloudwerk scans your app/ directory and compiles routes based on file conventions:
| File | Description |
|---|---|
page.tsx | UI for a route |
route.ts | API endpoint |
layout.tsx | Shared UI wrapper |
middleware.ts | Request middleware |
error.tsx | Error boundary |
not-found.tsx | 404 handler |
Basic Routes
Section titled “Basic Routes”Static Routes
Section titled “Static Routes”Create a page.tsx file to define a route:
Directoryapp/
- page.tsx # /
Directoryabout/
- page.tsx # /about
Directorycontact/
- page.tsx # /contact
// app/about/page.tsxexport default function AboutPage() { return <h1>About Us</h1>;}Nested Routes
Section titled “Nested Routes”Routes can be nested to any depth:
Directoryapp/
Directoryproducts/
- page.tsx # /products
Directorycategories/
- page.tsx # /products/categories
Directory[slug]/
- page.tsx # /products/categories/:slug
Dynamic Routes
Section titled “Dynamic Routes”Single Dynamic Segment
Section titled “Single Dynamic Segment”Use brackets [param] for dynamic segments:
app/users/[id]/page.tsx -> /users/:idAccess the parameter in your loader:
export async function loader({ params }: LoaderArgs) { const userId = params.id; // "123" for /users/123 return { user: await getUser(userId) };}Multiple Dynamic Segments
Section titled “Multiple Dynamic Segments”Routes can have multiple dynamic segments:
app/[locale]/blog/[slug]/page.tsx -> /:locale/blog/:slugexport async function loader({ params }: LoaderArgs) { const { locale, slug } = params; return { post: await getPost(locale, slug) };}Catch-All Segments
Section titled “Catch-All Segments”Use [...param] to match any number of segments:
app/docs/[...slug]/page.tsx| URL | params.slug |
|---|---|
/docs/intro | ['intro'] |
/docs/guides/routing | ['guides', 'routing'] |
/docs/api/core/types | ['api', 'core', 'types'] |
export async function loader({ params }: LoaderArgs) { const path = params.slug.join('/'); // "guides/routing" return { doc: await getDoc(path) };}Optional Catch-All
Section titled “Optional Catch-All”Use [[...param]] for optional catch-all segments:
app/shop/[[...categories]]/page.tsx| URL | params.categories |
|---|---|
/shop | undefined |
/shop/electronics | ['electronics'] |
/shop/electronics/phones | ['electronics', 'phones'] |
Route Groups
Section titled “Route Groups”Organize routes without affecting URLs using (groupName):
Directoryapp/
Directory(marketing)/
- layout.tsx # Marketing layout
- page.tsx # /
Directorypricing/
- page.tsx # /pricing
Directory(dashboard)/
- layout.tsx # Dashboard layout
Directorydashboard/
- page.tsx # /dashboard
Directorysettings/
- page.tsx # /settings
Parallel Routes
Section titled “Parallel Routes”Use @slot directories for parallel route segments:
Directoryapp/
- layout.tsx
Directory@sidebar/
- page.tsx
Directoryusers/
- page.tsx
Directory@main/
- page.tsx
Directoryusers/
- page.tsx
Access slots in your layout:
export default function Layout({ sidebar, main }: LayoutProps) { return ( <div className="flex"> <aside>{sidebar}</aside> <main>{main}</main> </div> );}Intercepting Routes
Section titled “Intercepting Routes”Intercept routes using (.), (..), or (...) prefixes:
Directoryapp/
Directoryfeed/
- page.tsx
Directory(.)photo/
Directory[id]/
- page.tsx # Intercepts /feed/photo/:id
Directoryphoto/
Directory[id]/
- page.tsx # Direct access to /photo/:id
| Convention | Description |
|---|---|
(.) | Same level |
(..) | One level up |
(...) | Root level |
API Routes
Section titled “API Routes”Create API endpoints with route.ts:
// app/api/users/route.tsimport { json } from '@cloudwerk/core';
export async function GET(request: Request, ctx: CloudwerkHandlerContext) { return json({ users: [] });}
export async function POST(request: Request, ctx: CloudwerkHandlerContext) { const body = await request.json(); return json({ created: body }, { status: 201 });}HTTP Methods
Section titled “HTTP Methods”Export functions for the HTTP methods you want to handle:
GET- Read dataPOST- Create dataPUT- Replace dataPATCH- Update dataDELETE- Remove dataHEAD- Headers onlyOPTIONS- CORS preflight
Route Priority
Section titled “Route Priority”When multiple routes could match, Cloudwerk uses this priority:
- Static routes (
/about) - Dynamic routes (
/[id]) - Catch-all routes (
/[...slug])
Next Steps
Section titled “Next Steps”- Data Loading - Server-side data fetching
- Middleware - Protect routes
- API Reference - Route configuration options