How Agent-Native Handles Dynamic Content: SSR, Route Discovery, and Runtime Loading
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 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.
// 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 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.
// 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 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.
// 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 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.
// 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 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.
// 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.tsloads React Router server builds dynamically viavirtual:react-router/server-buildto avoid 302 fallbacks. - Pattern-based discovery:
route-discovery.tsconverts[param]to:paramand 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.tsreloads 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. 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, 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →