Page Functions (page_functions.ts)
For advanced control over rendering behavior, data fetching, and static generation, you can create a page_functions.ts (or .js) file next to your page.tsx.
1. getProps (Static/Layout Data Injection)
Use this function to fetch data based on the route parameters and inject it into your Page and Root Layout.
Design Note
getProps only receives params. To use request-specific data like searchParams or cookies, fetch data directly inside your components using Suspense and Server Functions to avoid blocking the initial HTML render.- Arguments:
{ params }(The dynamic route parameters) - Returns: An object with
pageandlayoutkeys containing the props
// src/blog/[slug]/page_functions.ts
export async function getProps({ params }) {
// 1. Fetch data based on the URL path (e.g., /blog/my-post)
const post = await db.getPost(params.slug);
// 2. Return data.
// 'page' props go to page.jsx
// 'layout' props go to layout.jsx (Root Layout)
return {
page: { post },
layout: { title: post.title },
};
}2. getStaticPaths (Static Generation)
Defines which dynamic paths should be pre-rendered at server start (SSG).
Paths not returned here will be generated on-demand when requested for the first time.
Return Format
Dinou is flexible with the return format depending on the complexity of your route:
| Route Type | Best Format | Example Return |
|---|---|---|
Simple ([id]) | Array<string> | ["1", "2"] |
Catch-all ([...slug]) | Array<Array<string>> | [["a", "b"], ["c"]] |
| Nested / Complex | Array<Object> | [{"id": "1", "category": "tech"}] |
Simple Dynamic Routes
// src/blog/[id]/page_functions.ts
export function getStaticPaths() {
return ["1", "2", "hello"];
// Generates: /blog/1, /blog/2, /blog/hello
}Catch-all Routes
// src/docs/[...slug]/page_functions.ts
export function getStaticPaths() {
return [
["intro"], // /docs/intro
["api", "v1", "auth"], // /docs/api/v1/auth
];
}Automatic Route Propagation (Recursion)
One of Dinou's most powerful features is that static parameters propagate downwards. If you define values for a segment, Dinou will automatically generate all static sub-pages nested within that segment.
src/blog/[slug]/page.tsx (+ page_functions.ts)src/blog/[slug]/details/page.tsx (Nested static page)If getStaticPaths in blog/[slug] returns ["post-a", "post-b"], Dinou generates 4 pages:
/blog/post-a/blog/post-a/details/blog/post-b/blog/post-b/details
Nested Pages & The "Chain of Responsibility"
When nesting routes, dependency flows downwards. If an intermediate segment (whether static or dynamic) contains apage.tsx, it becomes a required step in the generation chain.
If a parent page fails to define its own paths (e.g., returns an empty array), the generator stops there. It will never reach the child pages, regardless of whether the children have valid getStaticPaths defined.
src/case3/[slug]/page.tsx (Parent Page)src/case3/[slug]/[id]/page.tsx (Child Page)In this structure, [id] depends physically on [slug] existing first.
If src/case3/[slug]/page_functions.ts returns [] (no paths):
- Dinou tries to build
/case3/[slug]. - No paths are returned. No folders are created.
- Result: The build process never attempts to generate
[id].
The parent must resolve its own level for the children to run:
// src/case3/[slug]/page_functions.ts
export function getStaticPaths() {
// 1. Defines the parent folders
return ["foo", "bar"];
}
// src/case3/[slug]/[id]/page_functions.ts
export function getStaticPaths() {
// 2. Now runs inside /case3/foo/ and /case3/bar/
return ["100", "200"];
}Rule of Thumb
page.tsx in the hierarchy is responsible for "opening the door" to its children.Nested & Complex Routes (Pass-through Segments)
When you have multiple dynamic segments in a path without intermediate pages, you must return an Object to map values to all parameter names involved.
// Structure: src/shop/[category]/[...specs]/[[brand]]/page_functions.ts
// (Assuming [category] and [...specs] do NOT have their own page.tsx)
export function getStaticPaths() {
return [
{
category: "electronics",
specs: ["m3", "16gb"],
brand: "apple",
},
{
category: "clothing",
specs: ["cotton", "white"],
brand: undefined, // Valid: optional and at the end of the route
},
];
}Reminder: No-Gap Rule
undefined for an intermediate optional segment only if all subsequent segments are also undefined. You cannot leave a "gap" (an undefined segment followed by a defined one).Normalization Guarantee
Dinou ensures that params are consistent between SSG and SSR:
Array (e.g., undefined becomes [], "val" becomes ["val"]).undefined if omitted.Async Support
getStaticPaths can be an async function. This allows you to fetch data from a database or API during the build process to determine which paths to render.
Perfect for generating product pages, blog posts, or categories based on your CMS content.
// src/products/[id]/page_functions.ts
export async function getStaticPaths() {
// Only generate the top 10 products at build time
const topProducts = await db.getTopProducts(10);
return topProducts.map(p => p.id);
// Any ID not in this list will be generated
// on-demand when a user visits /products/999
}3. revalidate (ISR)
Enables Incremental Static Regeneration. Defines the cache lifetime of a static page in milliseconds.
- Returns:
number(milliseconds) - If it returns
0(or is not defined), the page remains static indefinitely (unless rebuilt)
// src/dashboard/page_functions.ts
export function revalidate() {
return 60000; // Regenerate at most once every 60 seconds
}4. dynamic (Force SSR)
Forces a page to be rendered dynamically (Server-Side Rendering) on every request, bypassing static generation.
- Returns:
boolean
// src/profile/page_functions.ts
export function dynamic() {
return true; // Always render on demand (SSR)
}