Hydration Consistency and Deferred Data
Remix v2 provides no formal guarantee that deferred promises will resolve before or after the initial hydration process. Hydration occurs once based on the initial HTML and resolved data sent in the first chunk of the response. When a deferred promise resolves, React triggers a re-render specifically for the subtree wrapped in the <Await> component.
Component Dependency Limits
To ensure stability and avoid runtime errors during the transition from fallback to resolved state, components must adhere to these dependency boundaries:
- Safe Dependencies: Only components nested within
<Await resolve={promise}>...</Await> are guaranteed to receive the resolved value safely. These components only mount once the promise has successfully resolved.
- Unsafe Dependencies: Components accessing deferred keys via
useLoaderData() outside of an <Await> boundary will receive the raw Promise object. Attempting to treat this object as resolved data (e.g., mapping over it as an array) will cause the application to crash during hydration or the first render.
Experimental Status and Production Risk
Because the defer API is marked experimental, it is subject to changes in the streaming protocol and fallback handling across minor releases. Production use carries the risk that updates to the Remix framework may alter how the server serializes promises or how the client-side runtime handles the streamed response, potentially requiring manual updates to your <Await> implementations.
Verification Steps
To verify the behavior of your streaming implementation, use the following scoped tests:
- Network Inspection: Open the browser Network tab. A successful deferred request should show a single HTTP request that remains "Pending" while the HTML is rendered, followed by the arrival of the resolved JSON data in the same stream.
- Serialization Check: Ensure all deferred values are plain objects or arrays. Attempting to defer non-serializable types (like class instances or functions) will result in server-side errors or corrupted client payloads.
- Hydration Timing: Add
console.log statements inside a useEffect in both the Fallback component and the resolved child component to observe the sequence of mounting relative to the page load.
Diagnostic Detail Needed: Are you utilizing a custom ErrorBoundary to wrap your <Await> components? The handling of rejected deferred promises differs significantly depending on whether the error is caught within the <Await> subtree or by a route-level boundary.