AI Miniapps With Navigation

The generation pipeline now targets a bundle format: multiple React Native screen artifacts, one portable navigation manifest, and version metadata that keeps the runtime and KV publisher aligned.

Generated Artifact Shape

JSON
{
  "components": [
    {
      "key": "support/HomePage",
      "name": "HomePage",
      "fileName": "HomePage.tsx",
      "code": "export default function HomePage() { return null; }"
    },
    {
      "key": "support/DetailsPage",
      "name": "DetailsPage",
      "fileName": "DetailsPage.tsx",
      "code": "export default function DetailsPage() { return null; }"
    }
  ],
  "navigation": {
    "version": 1,
    "rootId": "root-stack",
    "nodes": [
      {
        "kind": "stack",
        "id": "root-stack",
        "name": "Root",
        "route": { "path": "/" },
        "initialRouteId": "home-page",
        "children": [
          {
            "kind": "page",
            "id": "home-page",
            "name": "HomePage",
            "componentKey": "support/HomePage",
            "route": { "path": "/" }
          },
          {
            "kind": "page",
            "id": "details-page",
            "name": "DetailsPage",
            "componentKey": "support/DetailsPage",
            "route": { "path": "/details" }
          }
        ]
      }
    ]
  }
}

Publishing Flow

  1. Validate every generated screen component independently.
  2. Validate the navigation graph against the generated component keys and the effective targetNavigationEngine.
  3. Store navigation and the resolved engine in version metadata.
  4. Write every screen artifact to Cloudflare KV.
  5. Save one miniapp index payload containing components[] and navigation.

Target Navigation Engine

Every codegen request resolves a targetNavigationEngine so the generator and validator speak the same language as the runtime that will render the miniapp. Resolution order (first match wins):

  1. targetNavigationEngine in the POST /api/codegen/generate request body.
  2. .dpage/project.json from dpage link in the workspace that issued the request.
  3. The project's server-side default from Project Settings → Navigation Engine.
  4. "manifest" (built-in fallback).

When the effective engine is:

  • manifest - No react-navigation-specific constraints. Icons on bottom tabs stay optional.
  • react-navigation - Every visible child of a bottomTabs navigator must declare a non-empty tabItem.icon (stack children must carry it on the initial page). Hidden tabs (tabItem.hidden: true) are exempt. Bundles that omit a required icon fail server validation and trigger the refinement loop. New bundles should also include tabItem.iconLibrary so the host can resolve the correct icon set.
  • auto - Let the host pick at runtime; non-strict validation. Icons are encouraged but not enforced.
JSON
{
  "navigation": {
    "version": 1,
    "rootId": "root-tabs",
    "nodes": [
      {
        "kind": "tabs",
        "id": "root-tabs",
        "variant": "bottomTabs",
        "initialRouteId": "home-page",
        "children": [
          {
            "kind": "page",
            "id": "home-page",
            "name": "Home",
            "componentKey": "bookhub/HomeScreen",
            "route": { "path": "/home" },
            "tabItem": {
              "icon": "home",
              "iconLibrary": "feather",
              "label": "Home"
            }
          },
          {
            "kind": "page",
            "id": "clubs-page",
            "name": "Clubs",
            "componentKey": "bookhub/BookClubsScreen",
            "route": { "path": "/clubs" },
            "tabItem": {
              "icon": "account-group",
              "iconLibrary": "material-community",
              "label": "Clubs"
            }
          }
        ]
      }
    ]
  }
}

Changing the engine later

Update the project-wide default from Project Settings → Navigation Engine, re-run dpage link to change a workspace's value, or pass targetNavigationEngine explicitly on a one-off codegen request.

Runtime Contract

The mobile runtime fetches the miniapp index from /m/:id. Each component entry still carries its fetched code in value, while the optional navigation field tells the runtime how to assemble those screens into a full app flow.

If the AI returns a single screen or the stored version predates portable navigation, the server falls back to a synthesized one-screen manifest so older miniapps remain valid.