# How Agent-Native Handles Dynamic Content: SSR, Route Discovery, and Runtime Loading

> Agent-Native efficiently manages dynamic content using SSR, lazy imports, and client-side recovery. Discover how its architecture optimizes API route discovery and runtime loading for better performance.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: deep-dive
- Published: 2026-06-21

---

**Agent-Native handles dynamic content through a three-layer architecture that uses lazy imports for server-side rendering, pattern-based route discovery for API endpoints, and client-side recovery scripts to manage failed dynamic imports.**

BuilderIO/agent-native treats dynamic content as any resource resolved at runtime rather than baked into the build. The framework isolates this behavior across server-side rendering, API route discovery, and client-side error recovery. This approach ensures that React Router pages, dynamic API segments, and optional peer dependencies load only when requested.

## Server-Side Rendering with Lazy Virtual Imports

The SSR layer defers loading the React Router server build until the first request hits the catch-all route. Instead of bundling the server build statically, the handler in [`packages/core/src/server/ssr-handler.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/server/ssr-handler.ts) uses a **dynamic `import()`** to load `virtual:react-router/server-build`.

### The Virtual Module Pattern

The virtual module lives inside the project template rather than `node_modules`, allowing Vite to apply its dev-plugin transformations. This avoids **302 fallback** errors that would occur if the import resolved from external dependencies.

```typescript
// packages/core/src/server/ssr-handler.ts
export default createH3SSRHandler(
  () => import('virtual:react-router/server-build')
);

```

The dynamic import wrapper caches the promise after the first load, ensuring subsequent requests reuse the compiled server build without re-executing the import.

## Dynamic Route Discovery for Deployment

Agent-Native parses Nitro-style file trees to identify API routes containing dynamic segments like `[id]` or `[...catchall]`. The `parseRouteFile` function in [`packages/core/src/deploy/route-discovery.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/deploy/route-discovery.ts) converts bracket notation into colon-prefixed parameters and flags routes as dynamic.

### Pattern-Based Route Parsing

When processing files such as `api/users/[id].get.ts`, the utility transforms `[param]` into `:param` notation. This ensures the deployment configuration recognizes these paths as dynamic functions rather than static assets.

```typescript
// packages/core/src/deploy/route-discovery.ts
import { parseRouteFile } from '@agent-native/core/deploy/route-discovery';
const info = parseRouteFile('api/users/[id].get.ts');
// → { method: 'get', route: '/api/users/:id', dynamic: true }

```

The generated Cloudflare or Netlify function config routes requests to server functions instead of cached HTML pages when dynamic segments are detected.

## Runtime Lazy Loading for Edge Compatibility

To maintain compatibility with edge runtimes where `require()` would crash, Agent-Native implements **lazy loading** for optional dependencies and heavy server plugins. The `getFs()` helper in [`packages/core/src/deploy/route-discovery.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/deploy/route-discovery.ts) demonstrates this pattern.

### The Lazy FS Loader Pattern

The helper caches the `fs` module promise after the first dynamic import, keeping the core bundle small and allowing execution in environments without native filesystem access.

```typescript
// packages/core/src/deploy/route-discovery.ts
let _fs: typeof import('fs') | undefined;

async function getFs() {
  if (!_fs) _fs = await import('node:fs');
  return _fs;
}

```

This approach applies to optional peer dependencies like the ACP SDK and scheduling UI, which load only when explicitly requested.

## Client-Side Error Recovery for Stale Chunks

Dynamic imports can fail when CDN chunks become stale after a deployment. Agent-Native includes a recovery script in [`packages/core/src/client/vite-dev-recovery-script.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/vite-dev-recovery-script.ts) that detects failed dynamic module fetches and reloads the page.

### Failed Import Detection

The script listens for unhandled rejections containing "Failed to fetch dynamically imported module". When detected, it either reloads the page or navigates to a fresh target, preventing frozen UI states.

```typescript
// packages/core/src/client/vite-dev-recovery-script.ts
if (err.message.includes('Failed to fetch dynamically imported module')) {
  location.reload();
}

```

This mechanism runs in both Vite dev server and production bundles, ensuring users always receive the latest chunks.

## Migration Guards for Headless Content

The migration tooling in [`packages/migrate/src/adapters/nextjs.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/migrate/src/adapters/nextjs.ts) enforces that pages fetched from headless CMS sources remain dynamic rather than being stored in Builder's content blobs. It scans for route patterns containing colons or wildcards.

### Dynamic Page Detection

The adapter checks path values for dynamic indicators like `:param` or `*`, ensuring these routes are served by approved headless sources rather than static generation.

```typescript
// packages/migrate/src/adapters/nextjs.ts
const isDynamic = pathValue.includes(":") || pathValue.includes("*");

```

This gate prevents accidental staticification of content that must resolve at runtime.

## Summary

- **Lazy SSR imports**: The [`ssr-handler.ts`](https://github.com/BuilderIO/agent-native/blob/main/ssr-handler.ts) loads React Router server builds dynamically via `virtual:react-router/server-build` to avoid 302 fallbacks.
- **Pattern-based discovery**: [`route-discovery.ts`](https://github.com/BuilderIO/agent-native/blob/main/route-discovery.ts) converts `[param]` to `:param` and marks routes as dynamic for edge function deployment.
- **Edge-compatible loading**: The `getFs()` helper uses cached dynamic imports to load Node.js modules only when needed.
- **Client recovery**: The [`vite-dev-recovery-script.ts`](https://github.com/BuilderIO/agent-native/blob/main/vite-dev-recovery-script.ts) reloads pages when stale chunks cause dynamic import failures.
- **Migration enforcement**: The Next.js adapter ensures headless CMS pages stay dynamic by detecting colon and wildcard patterns.

## Frequently Asked Questions

### How does Agent-Native load React Router for SSR?

Agent-Native uses a dynamic `import()` to load `virtual:react-router/server-build` inside the `createH3SSRHandler` function in [`packages/core/src/server/ssr-handler.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/server/ssr-handler.ts). This loads the compiled server bundle only when the first request hits the catch-all route, caching the result for subsequent requests.

### What pattern does Agent-Native use to identify dynamic API routes?

The framework parses Nitro-style file trees in [`packages/core/src/deploy/route-discovery.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/deploy/route-discovery.ts), converting bracket notation like `[id]` into colon-prefixed parameters like `:id`. Routes containing these patterns are flagged as dynamic, ensuring deployment targets route them to server functions rather than static assets.

### How does Agent-Native recover from failed dynamic imports on the client?

The [`vite-dev-recovery-script.ts`](https://github.com/BuilderIO/agent-native/blob/main/vite-dev-recovery-script.ts) monitors for unhandled rejections containing "Failed to fetch dynamically imported module". When detected, it triggers a page reload to fetch fresh chunks from the CDN, preventing UI freezes caused by stale JavaScript bundles.

### Why does Agent-Native use dynamic imports instead of require()?

Dynamic imports enable lazy loading of optional peer dependencies and Node.js built-ins like `fs` while maintaining compatibility with edge runtimes where `require()` would crash. This pattern keeps the core bundle small and allows the same code to execute in both Node.js and edge environments.