GitHubnpm

Router Options

All configuration options for createRouter(manifest, options?). Every field is optional. The router works with zero configuration.

RouterOptions

OptionTypeDefaultDescription
rootstring—Base URL for resolving module import() paths. Pass import.meta.url from your server entry.
basePathstring'/'URL prefix for all routes (e.g. '/app').
csrfProtectionboolean | { allowedOrigins?: string[] }trueCSRF validation on POST. Pass false to disable or an object with allowedOrigins.
onBeforeRequest(req) => Response | void—Hook called before routing. Return a Response to short-circuit.
onNotFound(req) => Response404 textHook called when no route matches.
onError(error, req) => Response500 textHook for unhandled errors. Thrown Responses never reach this hook.
requestTimeoutnumberundefinedMax request time in ms. Exceeding returns 504. Set to 0 to disable.
mode'development' | 'production''production'Controls error verbosity, module caching, and HMR injection.
hmrUrlstring'/@vite/client'Vite HMR WebSocket URL. Only used in development mode.
assetPrefixstring'/'Public URL prefix for client chunk paths.
clientEntrystring—Path to the client entry module that calls hydrateRoot().
streamingboolean | 'shell'falseHTML streaming mode. false = wait, 'shell' = recommended, true = full stream.
noncestring—CSP nonce for inline scripts. Passed to React's renderToReadableStream.
rscbooleanfalseEnable RSC payload negotiation for client-side SPA navigation.
cacheCacheAdapter—Cross-request cache adapter for CDN or KV-level deduplication.

CacheAdapter Interface

interface CacheAdapter {
  get(key: string): unknown | Promise<unknown>;
  set(key: string, value: unknown, ttl?: number): void | Promise<void>;
}

Platform-specific implementations: Cloudflare caches.default, Redis, KV stores, or an in-memory LRU.

NavigationOptions

Options for initNavigation(setPage, options?) from @cmj/juice/client.

OptionTypeDefaultDescription
viewTransitionsbooleantrueUse the View Transitions API for page transitions.

Full Example

import { createRouter } from '@cmj/juice/runtime';
import manifest from './flight-manifest.json';

export default {
  fetch: createRouter(manifest, {
    root: import.meta.url,
    basePath: '/',
    streaming: 'shell',
    nonce: crypto.randomUUID(),
    rsc: true,
    csrfProtection: {
      allowedOrigins: ['https://admin.example.com'],
    },
    requestTimeout: 30_000,
    cache: {
      get: (key) => globalCache.get(key),
      set: (key, value, ttl) => globalCache.set(key, value, { ttl }),
    },
    onBeforeRequest: (req) => {
      if (isMaintenanceMode()) {
        return new Response('Maintenance', { status: 503 });
      }
    },
    onNotFound: (req) => {
      return new Response('Page not found', { status: 404 });
    },
    onError: (error, req) => {
      console.error('[app]', req.url, error);
      return new Response('Internal error', { status: 500 });
    },
  }),
};