Skip to content

Package

remix/middleware/render

Request-scoped response rendering for Remix. It provides the conventional Remix UI renderer and a low-level escape hatch for custom renderers.

Features

  • Remix UI rendering - Stream nodes to HTML responses with render()
  • Framework-owned frames - Resolve nested and targeted <Frame> requests through the current router
  • Client entry assets - Resolve source-based clientEntry() modules and their preloads through an asset server
  • Typed context - Preserve renderer input and response option types on context.render
  • Custom renderers - Install JSON, email, or other response pipelines with renderWith()

Installation

npm i remix

Usage

Install render() in the router middleware stack. Pass an asset server when components use source-based client entries such as clientEntry(import.meta.url, Component).

import { createAssetServer } from 'remix/assets'
import { render } from 'remix/middleware/render'
import { staticFiles } from 'remix/middleware/static'
import { createRouter } from 'remix/router'
import { Frame } from 'remix/ui'

let assets = createAssetServer({
  basePath: '/assets',
  rootDir: process.cwd(),
  allowFiles: ['app/routes.ts', 'app/**/public/**'],
  allowPackages: ['remix'],
  denyFiles: ['app/**/*.test.*'],
})

let router = createRouter({
  middleware: [staticFiles('./public'), render({ assets })],
})

router.get(
  '/assets/*path',
  async ({ request }) =>
    (await assets.fetch(request)) ?? new Response('Not Found', { status: 404 }),
)

router.get('/', (context) =>
  context.render(
    <html>
      <body>
        <h1>Dashboard</h1>
        <Frame src="/activity" fallback={<p>Loading activity…</p>} />
      </body>
    </html>,
  ),
)

context.render(node, init) returns an HTML Response and preserves the supplied status and headers:

router.get('/missing', (context) =>
  context.render(<h1>Not found</h1>, {
    status: 404,
    headers: { 'Cache-Control': 'no-store' },
  }),
)

The middleware forwards request credentials and session headers to internal frame requests, converts them to safe GET requests, follows redirects, preserves application error bodies, propagates frame targets and top-frame URLs, and cancels frame rendering when the original request is aborted.

Options

  • assets - An asset server that resolves source-based client entry IDs to browser module URLs and preload URLs. Omit it when client entries already use public URLs or the app has no client entries.
  • onError - A callback for server rendering errors. When omitted, the UI renderer uses its default error reporting.

Custom renderers

Use renderWith() when the input is not a Remix UI node or the application owns a fully custom response pipeline. The factory runs once per request and may read the current request context.

import { renderWith } from 'remix/middleware/render'
import { createRouter } from 'remix/router'

let json = renderWith(
  () =>
    function render(data: unknown, init?: ResponseInit) {
      return Response.json(data, init)
    },
)

let router = createRouter({ middleware: [json] })

router.get('/api/status', (context) => context.render({ ok: true }))

Custom renderers are also available through context.get(Renderer) when direct-property access is not suitable.

  • assets - Source asset compilation and browser module URLs
  • fetch-router - Request routing and typed context
  • ui - Remix UI components, frames, and server rendering
  • response - Web Response helpers

License

See LICENSE