Dynamic Routes
Learn how to create dynamic routes with parameters, catch-all segments, and optional catch-all segments in Bini.js.
Overview
Dynamic routes allow you to create pages that match a pattern rather than a static path. This is essential for pages like blog posts, product pages, user profiles, and documentation.
Single parameter routes with [param]
Match multiple segments with [...]
Optional multi-segment routes with [[...]]
useParams() is auto-imported in all pages and layouts — no import statement needed.Dynamic Segments
Create a dynamic segment by wrapping a folder or file name in square brackets: [name]. The parameter name must match /^[a-zA-Z_][a-zA-Z0-9_]*$/.
Dynamic Folder Example
Accessing Parameters
Access the parameter value using useParams(), which is auto-imported:
With Data Fetching
Multiple Parameters
You can have multiple dynamic segments in a single route. Each segment becomes a property in the useParams() object.
Example with Multiple Params
| URL | params |
|---|---|
| /blog/tech/hello-world | { category: "tech", slug: "hello-world" } |
| /blog/lifestyle/travel | { category: "lifestyle", slug: "travel" } |
| /blog/design/ux-tips | { category: "design", slug: "ux-tips" } |
Catch-all Segments
Use [...name] to match any number of segments. The parameter becomes an array of the matched segments. This is perfect for documentation, file paths, or any multi-level navigation.
Catch-all Example
| URL | slug value |
|---|---|
| /docs/getting-started | ['getting-started'] |
| /docs/api/reference | ['api', 'reference'] |
| /docs/guides/routing/basics | ['guides', 'routing', 'basics'] |
| /docs/advanced/custom/hooks | ['advanced', 'custom', 'hooks'] |
Flat File Catch-all
/blog/featured will match a static route if it exists, falling back to the catch-all only if no more specific route matches.Optional Catch-all Segments
Use [[...name]] to make the catch-all optional. The route matches even without any segments, making it perfect for multi-level navigation like documentation or shop categories.
Optional Catch-all Example
| URL | slug value |
|---|---|
| /shop | undefined |
| /shop/clothing | ['clothing'] |
| /shop/clothing/shirts | ['clothing', 'shirts'] |
| /shop/electronics/phones/iphone | ['electronics', 'phones', 'iphone'] |
Dynamic Segments in Layouts
Layouts can also access dynamic parameters using useParams(), which is auto-imported. This is useful for displaying contextual information in headers, sidebars, or breadcrumbs.
Layout with Dynamic Params
File Structure
Flat File Dynamic Routes
Dynamic routes can also be created as flat files without folders. This is especially useful for simpler pages where a folder structure would be unnecessary overhead.
Flat File Examples
When to Use Flat Files
- Simple pages that don't need nested layouts
- API routes with dynamic parameters
- Single-level dynamic pages (e.g.,
/post/:id) - When you want to reduce folder nesting
Route Priority
When multiple routes could match a URL, Bini.js resolves them in this order:
- Static routesexact matches — e.g.,
/blog/featured - Dynamic single segments
[slug]— e.g.,/blog/:slug - Catch-all segments
[...slug]— e.g.,/blog/* - Optional catch-all segments
[[...slug]]— e.g.,/docs/*(optional)
Priority Example
Consider this folder structure with overlapping routes:
| URL | Matched Route | Priority |
|---|---|---|
| /blog/featured | featured/page.tsx | Static |
| /blog/hello-world | [slug]/page.tsx | Dynamic |
| /blog/a/b/c | [...slug]/page.tsx | Catch-all |
| /blog/latest/post | [slug]/page.tsx | Dynamic |
This ensures predictable routing behavior and prevents conflicts between different route types.
Complete Example
Here is a comprehensive example showing all dynamic route patterns in a real-world application:
Route Mapping
| Pattern | Example URL | Type |
|---|---|---|
| /blog/featured | /blog/featured | Static |
| /blog/:slug | /blog/hello-world | Dynamic Single |
| /blog/:category/:slug | /blog/tech/hello-world | Multiple Dynamic |
| /blog/* | /blog/a/b/c | Catch-all |
| /docs/* (optional) | /docs | Optional Catch-all |
| /docs/* (optional) | /docs/getting-started | Optional Catch-all |
| /products/:id | /products/123 | Flat File Dynamic |
| /users/:userId/settings | /users/john/settings | Nested Dynamic |