[GH-ISSUE #654] RSC parity gap: action redirects use hard navigation instead of soft RSC navigation #136

Open
opened 2026-05-06 12:37:34 +02:00 by BreizhHardware · 1 comment

Originally created by @southpolesteve on GitHub (Mar 22, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/654

Background

When a server action calls redirect(), the server (in app-rsc-entry.ts) catches the redirect and returns a response with an empty body and an x-action-redirect header. The client currently detects this header and performs a hard navigation (location.assign() / location.replace()) as a workaround.

This is tracked as a known parity gap, introduced in #620 which fixed #589.

What Next.js does

Next.js pre-renders the redirect target's RSC payload as part of the action response body. The client then uses __VINEXT_RSC_NAVIGATE__ (or equivalent) to perform a soft RSC navigation, keeping the existing page shell and only swapping the route content, without a full browser reload.

Current vinext behavior

Full page reload on every server action redirect. This is functionally correct but:

  • Causes a flash of unstyled/loading content on redirect
  • Loses any in-memory client state not persisted to the URL
  • Breaks the expected SPA navigation feel
  • Is inconsistent with how router.push() and link clicks work

What needs to change

In app-rsc-entry.ts (around line 2060), when a server action response catches a NEXT_REDIRECT, the server should:

  1. Render the redirect target route to an RSC stream
  2. Send that stream as the response body (instead of an empty body)
  3. Set x-action-redirect and x-action-redirect-type headers as it does now

The client in app-browser-entry.ts can then remove the hard redirect fallback and resume using __VINEXT_RSC_NAVIGATE__ for all same-origin action redirects.

References

  • Fixed by (workaround): #620
  • Original bug report: #589
  • Relevant code: packages/vinext/src/entries/app-rsc-entry.ts ~line 2060, packages/vinext/src/server/app-browser-entry.ts ~line 144

/cc @yunus25jmi1

Originally created by @southpolesteve on GitHub (Mar 22, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/654 ## Background When a server action calls `redirect()`, the server (in `app-rsc-entry.ts`) catches the redirect and returns a response with an empty body and an `x-action-redirect` header. The client currently detects this header and performs a hard navigation (`location.assign()` / `location.replace()`) as a workaround. This is tracked as a known parity gap, introduced in #620 which fixed #589. ## What Next.js does Next.js pre-renders the redirect target's RSC payload as part of the action response body. The client then uses `__VINEXT_RSC_NAVIGATE__` (or equivalent) to perform a soft RSC navigation, keeping the existing page shell and only swapping the route content, without a full browser reload. ## Current vinext behavior Full page reload on every server action redirect. This is functionally correct but: - Causes a flash of unstyled/loading content on redirect - Loses any in-memory client state not persisted to the URL - Breaks the expected SPA navigation feel - Is inconsistent with how `router.push()` and link clicks work ## What needs to change In `app-rsc-entry.ts` (around line 2060), when a server action response catches a `NEXT_REDIRECT`, the server should: 1. Render the redirect target route to an RSC stream 2. Send that stream as the response body (instead of an empty body) 3. Set `x-action-redirect` and `x-action-redirect-type` headers as it does now The client in `app-browser-entry.ts` can then remove the hard redirect fallback and resume using `__VINEXT_RSC_NAVIGATE__` for all same-origin action redirects. ## References - Fixed by (workaround): #620 - Original bug report: #589 - Relevant code: `packages/vinext/src/entries/app-rsc-entry.ts` ~line 2060, `packages/vinext/src/server/app-browser-entry.ts` ~line 144 /cc @yunus25jmi1
Author
Owner

@yunus25jmi1 commented on GitHub (Mar 25, 2026):

Hi @southpolesteve @james-elicx — just checking whether either of you is already working on this. If not, I can take it on.
— @yunus25jmi1

<!-- gh-comment-id:4126671366 --> @yunus25jmi1 commented on GitHub (Mar 25, 2026): Hi @southpolesteve @james-elicx — just checking whether either of you is already working on this. If not, I can take it on. — @yunus25jmi1
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#136
No description provided.