Portable Navigation
Portable miniapp navigation lets a miniapp describe its own route graph, while the host app chooses whether to render it with the built-in portable renderer or React Navigation.
What Ships In The Manifest
A manifest-backed miniapp now has two parts: a components[] array containing the compiled screen artifacts, and a navigation object describing how those screens behave as an app.
{
"version": 1,
"rootId": "root-tabs",
"nodes": [
{
"kind": "tabs",
"id": "root-tabs",
"name": "Root",
"route": { "path": "/" },
"variant": "bottomTabs",
"initialRouteId": "home-stack",
"children": [
{
"kind": "stack",
"id": "home-stack",
"name": "Home",
"route": { "path": "/home", "title": "Home" },
"initialRouteId": "home-page",
"children": [
{
"kind": "page",
"id": "home-page",
"name": "HomePage",
"componentKey": "demo/HomePage",
"route": { "path": "/home" }
}
]
}
]
}
]
}Authoring Rules
- Every route id must be unique.
- Every page path must be unique.
rootIdmust point to a stack or tabs navigator, not a page.- Every navigator
initialRouteIdmust reference one of its direct children. - Every page
componentKeymust exist incomponents[]. modalandfullScreenModalpages must live under a stack.
Target Navigation Engine
Each project (and each codegen request) declares a targetNavigationEngine so the server can validate the manifest against the rules of the runtime that will render it.
- manifest - Default. Portable router; no react-navigation-specific constraints.
- react-navigation - Strict: every visible child of a
bottomTabsnavigator must declare a non-emptytabItem.icon. For stack children, the requirement applies to the stack's initial page. To omit an icon, hide the tab withtabItem.hidden: true. When present,tabItem.iconLibrarytells the host which icon pack to use. - auto - Let the host decide at runtime; non-strict validation.
Resolution order (first match wins): targetNavigationEngine on the codegen request → .dpage/project.json from dpage link→ the project's server default (Project Settings → Navigation Engine) → manifest.
{
"kind": "tabs",
"id": "root-tabs",
"variant": "bottomTabs",
"initialRouteId": "home-page",
"children": [
{
"kind": "page",
"id": "home-page",
"name": "Home",
"componentKey": "app/HomeScreen",
"route": { "path": "/home" },
"tabItem": {
"icon": "home",
"iconLibrary": "feather",
"label": "Home"
}
},
{
"kind": "page",
"id": "profile-page",
"name": "Profile",
"componentKey": "app/ProfileScreen",
"route": { "path": "/profile" },
"tabItem": {
"icon": "account-circle-outline",
"iconLibrary": "material-community",
"label": "Profile"
}
}
]
}Why icons become required
react-navigation. Projects targeting manifest (the default) or auto keep icons optional. tabItem.iconLibrary is optional for legacy manifests but recommended for new ones.Page Params Contracts
Pages can declare structured param contracts with paramsSchema and optional initialParams. Navigation calls validate params before route state changes.
{
"kind": "page",
"id": "book-details-page",
"name": "BookDetailsPage",
"componentKey": "books/Details",
"route": { "path": "/book-details" },
"paramsSchema": {
"type": "object",
"properties": {
"bookId": { "type": "string" },
"page": { "type": "number" }
},
"required": ["bookId"],
"additionalProperties": false
},
"initialParams": { "page": 1 }
}const router = useMiniappRouter();
router.push('/book-details', { bookId: 'book-123', page: 2 });
router.back();
const route = useMiniappRoute<{ bookId: string; page?: number }>();
console.log(route.params.bookId);Validation behavior
paramsSchema, invalid initialParams, or invalid runtime params are rejected during normalization/navigation instead of entering the stack with bad state.Generation checklist
router.back(), not router.goBack() or navigation.goBack(). Every route.params.someKey access must also have someKey declared in that page node's paramsSchema.properties, for example membersJson when reading route.params.membersJson.Host Integration Matrix
| Engine | Best For | Notes |
|---|---|---|
| Portable renderer | Zero-host-setup embeds and demo flows | Supports manifest theme options plus renderPortableHeader and renderPortableTabBar. |
| React Navigation renderer | Native stacks/tabs and deeper host integration | Honors portable chrome plus nativeOptions.reactNavigation. Stack headers are hidden by default unless the manifest explicitly opts in or provides header styling. |
| Expo Router adapter | Coordinating miniapp state with host router URLs | Keeps dpage as the route owner while letting the host router mirror path changes. |
Runtime Example
import { MiniappComponent } from '@dpage-ai/react-native-dpage';
<MiniappComponent
miniappId="support-workflow"
initialTarget="/home/details"
navigationEngine="auto"
/>;Offline And Cache Behavior
Multi-page miniapps keep their normalized manifest and initial path in miniapp cache metadata. That means an already-cached miniapp can still boot into the correct route graph when the network is unavailable.
Legacy miniapps still work
navigation field, the runtime automatically synthesizes a one-screen stack manifest from the old root: true contract.AI-Generated Miniapps
AI-generated miniapps can now publish multiple component artifacts plus one portable manifest. The publish pipeline writes every screen to KV, rewrites the manifest to the published component keys, and stores the final bundle in the miniapp index.