Skip to content

RFC: Add renderToResponse helper #890

@justinvdm

Description

@justinvdm

Problem

renderToStream is fundamentally designed for generating an HTML stream. It is not designed to handle the full lifecycle of a RedwoodSDK app route (which requires negotiating between HTML and RSC responses).

Trying to force renderToStream to handle RSC requests breaks its contract and confuses the user consumption model (stream vs response headers).

Instead of patching renderToStream, we need a new API (e.g., renderToResponse) that encapsulates the entire response generation logic (negotiating Content-Type and body format).

Proposed Solution

Introduce a new high-level helper: renderToResponse.

export function renderToResponse(
  element: ReactElement,
  options: RenderOptions
): Promise<Response>

This helper will:

  1. Accept the component tree and standard options (Document, requestInfo, etc.).
  2. Inspect requestInfo to determine if the request is an RSC request (Action or Client Transition).
  3. If RSC Request:
    • Generate the raw RSC stream (including Action results).
    • Return a Response with Content-Type: text/x-component.
  4. If HTML Request:
    • Generate the HTML stream (with injected RSC payload).
    • Return a Response with Content-Type: text/html.

This gives us

  • Safe Default: Users get the correct behavior for both initial loads and interactivity without manual configuration.
  • DRY: This logic is currently embedded in internal logic for defineApp. Extracting it to a public helper allows "manual" routes to have feature parity with standard render() routes.
  • Clear API Boundary: renderToStream remains a low-level primitive for "give me HTML", while renderToResponse becomes the standard for "render this page" when users need to do so programmatically/imperatively instead of the default declarative route() API.

Example Usage

// worker.tsx
route("/my-custom-route", async (requestInfo) => {
  // Before: Crashes on interaction
  // return new Response(await renderToStream(<MyPage />, { Document }), { ... });

  // After: Works with Actions/Transitions
  return renderToResponse(<MyPage />, { Document, requestInfo });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    1.xIssues planned for a minor release after 1.0

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions