Routing System
Learn about dinou's file-based routing system and how to create different types of routes.
Overview
dinou uses a file-based routing system where routes are defined by creating page.tsx
files in folders. The routing system supports:
- Static routes
- Dynamic routes with parameters
- Optional dynamic routes
- Catch-all routes
- Nested layouts
- Custom not found pages
- Custom error pages
- Parallel routes (slots)
- Route groups
Base Directory
All routing in dinou is defined relative to the src/
directory. The structure and naming of files inside src/
determine how routes are resolved and rendered.
Here's a summary of the special files used in routing:
page.tsx
: Defines a route. The file path corresponds to the route path.layout.tsx
: Wraps a page or nested pages/layouts. Used for persistent UI such as headers, footers, sidebars, etc, or for defining the html document.not_found.tsx
: Defines a custom "not found" page for unmatched routes.error.tsx
: Defines an error page for the route. It receivesparams
,query
, anderror
props.- Slots: Folders starting with
@
, such as@sidebar
, define parallel content and must contain apage.tsx
file.
src/
├── page.tsx # Route: /
├── layout.tsx # Layout for /
├── not_found.tsx # 404 page for /
├── error.tsx # Error page for /
├── about/
│ └── page.tsx # Route: /about
└── @sidebar/
└── page.tsx # Slot for sidebar content
This convention keeps your routing declarative and consistent while supporting advanced use cases like nested layouts and error boundaries.
Static Routes
Static routes are the simplest type of route, defined by folder structure:
src/
├── page.tsx # → /
├── about/
│ └── page.tsx # → /about
└── blog/
├── page.tsx # → /blog
└── post/
└── page.tsx # → /blog/post
Each page.tsx
file exports a React component (client or server component):
// src/about/page.tsx
"use client";
export default function Page() {
return <h1>About Us</h1>;
}
Dynamic Routes
Dynamic routes are created using square brackets in folder names. The parameter value is passed to your component as props:
src/blog/[id]/page.tsx # → /blog/:id
// src/blog/[id]/page.tsx
"use client";
export default function Page({
params
}: {
params: { id: string }
}) {
return <h1>Blog Post: {params.id}</h1>;
}
Accessing /blog/123
will pass { params: { id: "123" } }
to the component.
Optional Dynamic Routes
Optional dynamic routes use double square brackets [[param]]
and match both with and without the parameter:
src/blog/[[category]]/page.tsx
This matches both:
/blog
→{ params: { category: undefined } }
/blog/tech
→{ params: { category: "tech" } }
// src/blog/[[category]]/page.tsx
"use client";
export default function Page({
params
}: {
params: { category?: string }
}) {
return (
<h1>
{params.category ? `Category: ${params.category}` : 'All Posts'}
</h1>
);
}
Catch-All Routes
Catch-all routes capture multiple path segments as an array:
Catch-All Routes [...param]
src/wiki/[...slug]/page.tsx # → /wiki/*
Examples:
/wiki/a
→{ params: { slug: ["a"] } }
/wiki/a/b/c
→{ params: { slug: ["a", "b", "c"] } }
Optional Catch-All Routes [[...param]]
src/wiki/[[...slug]]/page.tsx # → /wiki or /wiki/*
Examples:
/wiki
→{ params: { slug: [] } }
/wiki/a/b
→{ params: { slug: ["a", "b"] } }
// src/wiki/[[...slug]]/page.tsx
"use client";
export default function Page({
params
}: {
params: { slug: string[] }
}) {
return (
<div>
<h1>Wiki</h1>
<p>Path: /{params.slug.join('/')}</p>
</div>
);
}
Layouts
Layouts wrap page content and can be nested. Create a layout.tsx
file in any folder to define a layout:
// src/blog/layout.tsx
"use client";
import type { ReactNode } from "react";
export default function Layout({
children
}: {
children: ReactNode
}) {
return (
<div>
<nav>Blog Navigation</nav>
<main>{children}</main>
</div>
);
}
no_layout
file (without extension) in the same folder as your page.reset_layout
file (without extension) in the same folder as your nested layout to make it the first applied. This is useful for example when you have a landing page in /
.Parallel Routes (Slots)
Parallel routes, also known as slots, are defined using directory names that start with @
, such as @sidebar
. These slots are passed into layouts as props, allowing you to render multiple parts of a page in parallel.
// File structure:
src/@sidebar/page.tsx
src/page.tsx
src/layout.tsx
The content from @sidebar/page.tsx
is passed into layout.tsx
as props.sidebar
. You can render the slot in your layout like this:
"use client";
import type { ReactNode } from "react";
export default function Layout({
children,
sidebar,
}: {
children: ReactNode;
sidebar: ReactNode;
}) {
return (
<html lang="en">
<head>
<title>dinou app</title>
</head>
<body>
{sidebar}
{children}
</body>
</html>
);
}
Slots are especially useful when rendering persistent UI components like sidebars, headers, or other parallel content areas that accompany the main page content.
Not Found Pages
Create custom 404 pages by adding not_found.tsx
files:
// src/not_found.tsx
"use client";
export default function Page() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
</div>
);
}
If multiple not_found.tsx
files exist in a route hierarchy, the most nested one will be used.
no_layout
or no_layout_not_found
file (without extension) is found in the same folder as your not found page.Error Handling
Create custom error pages by adding error.tsx
files:
// src/error.tsx
"use client";
export default function Page({
error: { message, stack },
}: {
error: Error;
}) {
return (
<main className="flex-1 flex flex-col items-center justify-center p-4">
<div className="max-w-md w-full text-center space-y-6">
<h1 className="text-3xl font-bold text-red-600">Error</h1>
<p className="text-lg text-gray-700">
An unexpected error has occurred. Please try again later.
</p>
<a
href="/"
className="inline-block px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
>
Go to Home
</a>
</div>
<div className="mt-6 text-sm text-gray-500">
<pre className="whitespace-pre-wrap break-words">{message}</pre>
<pre className="whitespace-pre-wrap break-words">{stack}</pre>
</div>
</main>
);
}
The most nested error.tsx
file in a route hierarchy will be used. In production, if no error page is present, the error will be written to the console. In development, a default debug error page is shown.
no_layout
or no_layout_error
file (without extension) is present in the same folder as the error.tsx
page.error.tsx
pages. These are rendered dynamically and delaying rendering is discouraged. Use Suspense
if needed.The error page receives three props: params
, query
, and error
. The error
object contains a message
and a stack
, both strings.
Route Groups
Route groups are defined using directory names wrapped in parentheses, such as (group)
. They allow you to organize routes in your filesystem without affecting the URL structure.
// src/(auth)/login/page.tsx → "/login"
// src/(auth)/signup/page.tsx → "/signup"
The directory name (auth)
is ignored in the final URL, so both pages above will be rendered at the root level:/login
and /signup
.
This is useful for grouping related pages (like authentication-related routes) together in the codebase without adding a prefix to the URL path.