Migration & Changes from v4
Dinou v5 introduces a major internal rendering architecture redesign and loosens Server Component constraints to align more closely with native React behavior.
1. Architectural Refactor: Transition to React Flight
Dinou v5 completely overhauls the internal communication layer used during initial Server-Side Rendering (SSR).
createFromNodeStream to render the HTML.- Unified Protocol: Consolidates both initial SSR and client-side page transitions under a single, native React Flight streaming protocol, removing the need for a separate JSON-based bridge.
- True Stream Processing: The child process can now parse and render HTML chunks incrementally as they arrive, rather than waiting for a full JSON payload to be fully generated and deserialized.
- Improved Robustness: Eliminates the custom JSX-to-JSON serialization code, delegating all prop serialization (including complex nested structures or React references) directly to React's native, battle-tested engine.
Show Technical Details (for Ejected Code)
In an ejected Dinou v5 project, the React Flight rendering and IPC pipeline is implemented across the following modules in dinou/core/:
dinou/core/server.js: Serves as the main HTTP entry. When a page request arrives, it obtains the JSX tree (viaget-jsx.js) and serializes it usingrenderToPipeableStreamfrom@roggc/react-server-dom-esm/server.dinou/core/render-html.js: Runs as a child process. It receives the streamed Flight payload from the parent process over the child process IPC, deserializes it usingcreateFromNodeStreamfrom@roggc/react-server-dom-esm/client, and pipe-renders it to HTML using ReactDOM's server renderer.
⚠️ Breaking Change: useSearchParams() & usePathname() are Client-Only
react-server condition), modules marked with "use client" cannot be executed on the server by Server Components. Calling useSearchParams() or usePathname() inside a Server Component will now throw a runtime crash.- In Client Components: These hooks continue to work normally (ensure
"use client";is declared at the top of the file). - In Server Components: Access the pathname via
getContext().req.pathand query parameters viagetContext().req.queryinstead of calling the hooks.
2. Synchronous Server Components
In previous versions of Dinou, all Server Components had to follow strict rules. In v5, constraints are lifted to provide more flexibility.
Every Server Component was strictly required to be declared as an async function, even if it did not perform asynchronous operations.
// v4: Force async even for static components
export default async function Page() {
return <h1>Hello World</h1>;
}This restriction has been removed. A Server Component only needs to be async if it uses await in its body. Synchronous components work natively.
// v5: Pure synchronous components are supported
export default function Page() {
return <h1>Hello World</h1>;
}3. New APIs in page_functions
Dinou introduces new optional exports inside the page_functions.ts (or .js) files to give you finer control over dynamic routes, parameter validation, and cache tagging:
Allows dynamic route parameters validation (e.g., checking UUID formats or numeric IDs) before serving the page. Returning false will instantly abort the request and respond with a 404, bypassing render engines and data fetching.
// Validate id parameters are strictly numeric
export function validateParams(params) {
return typeof params.id === "string" && /^\d+$/.test(params.id);
}Allows opting out of Incremental Static Generation (ISG). If it returns false, any dynamic path that was not generated at server startup (via getStaticPaths) will return a 404 response on the spot instead of generating on-demand.
// Disable on-demand generation; serve getStaticPaths ONLY
export function allowISG() {
return false;
}Show Technical Details (for Ejected Code)
In the ejected core, these control flags are processed inside the routing middleware in dinou/core/server.js:
- Caching: The page functions are imported dynamically and cached inside
pageFunctionsConfigCacheto avoid redundant disk reads during production requests. - Request Interception: The Express route handler checks the parsed
validateParamsandallowISGconfigurations before calling the rendering child processes. If a block is triggered, it exits early with a 404.
Assigns custom static cache tags to the generated route page to enable granular cache purging and on-demand regeneration via revalidateTag.
// Assign cache tags to the static page cache
export async function getCacheTags(params) {
return ["products", `product-${params.id}`];
}4. Built-in Anti-Bot Shield (Express Middleware)
Dinou v5 introduces an integrated Anti-Bot Shield middleware inside server.js to protect applications using dynamic Incremental Static Generation (ISG).
Security & Performance Protection
Vulnerability scanning bots routinely crawl web servers searching for typical targets (like .php scripts, .env files, backup files, or paths like /wp-admin).
The Risks in ISG:
- CPU & Memory Exhaustion (Short Term): Since ISG attempts to compile unknown paths on-demand by spawning background child processes, a concurrent bot scan of thousands of fake routes would fork too many processes at once, spiking CPU usage to 100% and causing a Denial of Service (DoS).
- Disk Space Inflation (Long Term): Successful ISG builds are written to the server's filesystem (saving both
index.htmlandrsc.rscfiles). Letting bots generate static files for thousands of arbitrary garbage paths would eventually fill up the server's disk space.
The Shield Solution:
The middleware intercepts requests matching common bot scanning patterns and returns an instant 404 Not Found, completely bypassing the ISG compilation process and safeguarding system stability.
If you are developing a route that matches these patterns (for instance, if you require a page ending in .php or using paths like wp-admin), the server will block it and return a 404 by default. You can adjust the botGarbagePatterns list in dinou/core/server.js if you need to bypass this protection for specific routes.
Show Technical Details (for Ejected Code)
In the ejected dinou/core/server.js, the bot shield is implemented as an Express middleware placed immediately after cookieParser() and before any dynamic route resolution logic.
5. Minor Adjustments for Legacy Imports (Isolated Cases)
Dinou v5 enforces modern ES Modules (ESM) resolution standards.
ES Modules Resolution Notice
.js at the end of the import statement) to satisfy the resolver.This is only necessary for non-compliant third-party code and does not affect the standard source code of your application.
6. On-Demand Revalidationv5.1.0+
Dinou v5.1.0 introduces native support for On-Demand Revalidation. This allows you to purge and regenerate the static page cache of individual pages on-demand without rebuilding the entire application.
1. Assign Cache Tags in page_functions
To tag static page cache files for granular invalidations, export a getCacheTags function (which can be sync or async) from your route's page_functions.ts file.
// src/products/[id]/page_functions.ts
export async function getCacheTags(params) {
// Can perform async checks (e.g., query database or CMS)
const product = await db.getProduct(params.id);
return ["products", product.category, `product-${params.id}`];
}2. Trigger Revalidation in Server Functions (Primary Use Case)
The primary and recommended way to trigger on-demand revalidation is inside Server Functions (Server Actions) immediately after performing database updates or data mutations. This ensures the static cache is instantly updated on the next request.
// src/actions/update-product.js
"use server";
import { revalidateTag } from "dinou/server";
import { db } from "@/db";
export async function updateProduct(productId, data) {
// 1. Mutate the data in database
await db.updateProduct(productId, data);
// 2. Trigger on-demand revalidation on the server
// This will purge and rebuild all static pages tagged with this ID
await revalidateTag(`product-${productId}`);
}