Skip to content

fix: resolve relative server action redirects#1579

Open
NathanDrake2406 wants to merge 3 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/server-action-relative-redirect
Open

fix: resolve relative server action redirects#1579
NathanDrake2406 wants to merge 3 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/server-action-relative-redirect

Conversation

@NathanDrake2406
Copy link
Copy Markdown
Contributor

Overview

Item Details
Goal Match Next.js server action redirect semantics for dot-relative redirects.
Core change Resolve action redirect locations against the action initiation pathname and keep same-origin redirects on the App Router navigation path.
Boundary Browser-visible App Router behavior, with focused helper coverage for URL resolution.
Primary files packages/vinext/src/server/app-browser-action-result.ts, packages/vinext/src/server/app-browser-entry.ts, tests/e2e/app-router/nextjs-compat/server-actions-relative-redirect.spec.ts
Impact Nested server actions that call redirect("./...") or redirect("../...") land on the same routes as Next.js and do not force a document reload for same-origin App Router navigation.

Why

Server action redirect locations are relative to the route that initiated the action, not to an arbitrary later browser document URL, and same-origin action redirects should remain in the App Router client navigation lifecycle. The previous path treated the raw x-action-redirect value as a hard navigation target, which could resolve nested ./subpage redirects incorrectly and drop client state.

Area Principle / invariant What this PR changes
URL resolution Dot-relative action redirects resolve with Next.js directory-style semantics from the action route. Adds a typed resolver for server action redirect locations.
Client navigation Same-origin App Router action redirects should not require a document reload. Routes internal redirects through the app navigation runtime when available.
Compatibility tests Regression coverage should prove the user-visible contract. Ports the upstream browser scenario into the app-router compat fixture.

What changed

Scenario Before After
redirect("./subpage") from a nested route Could land on the parent route's subpage or reload the document. Lands on the nested subpage, matching Next.js.
redirect("../subpage") from a nested route Hard navigation could lose client state. Uses App Router navigation and preserves the in-page marker.
External action redirect Hard navigation. Still hard navigation.
Validation
  • PLAYWRIGHT_PROJECT=app-router pnpm run test:e2e -- tests/e2e/app-router/nextjs-compat/server-actions-relative-redirect.spec.ts
  • vp test run tests/app-browser-entry.test.ts -t "server action"
  • vp fmt --check packages/vinext/src/server/app-browser-action-result.ts packages/vinext/src/server/app-browser-entry.ts tests/app-browser-entry.test.ts tests/e2e/app-router/nextjs-compat/server-actions-relative-redirect.spec.ts tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/actions.ts tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/page.tsx tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subdir/page.tsx tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subdir/subpage/page.tsx tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subpage/page.tsx
  • vp env exec --node 24 ./scripts/run-nextjs-deploy-suite.sh /Users/nathan/Projects/vinext/.refs/nextjs-v16.2.6 --retries 0 -c 1 --debug test/e2e/app-dir/server-actions-relative-redirect/server-actions-relative-redirect.test.ts
  • vp check
  • commit hook: format, lint/typecheck, knip --no-progress
Risk / compatibility
  • Public API: no new public API.
  • Runtime: same-origin action redirects now use the App Router navigation runtime when present, which matches existing client navigation behavior.
  • External redirects: remain hard navigations.
  • Fallback behavior: parse fallback remains a hard navigation after the existing dangerous-scheme guard.

References

Reference Why it matters
Next.js upstream test Defines the browser-visible compatibility scenario ported here.
Next.js action handler Shows action redirects resolving relative locations from the current pathname before rendering the redirect result.
Next.js assignLocation Shows client-side handling of dot-relative navigation locations using a directory-style base URL.

Copilot AI review requested due to automatic review settings May 23, 2026 06:28
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 23, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1579

commit: fb66919

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Aligns vinext’s Server Action redirect handling with Next.js semantics for dot-relative locations, ensuring redirects resolve against the action initiation route and that same-origin redirects can stay on the App Router client navigation path (avoiding document reloads).

Changes:

  • Added resolveServerActionRedirectLocation() to resolve dot-relative redirect targets against the server action initiation URL.
  • Updated browser server-action redirect handling to route same-origin redirects through the navigation runtime when available, falling back to hard navigations otherwise.
  • Added unit + Playwright E2E coverage and fixture pages for relative/absolute/multi-level redirects.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subpage/page.tsx Adds a target page for root-level relative redirect assertions.
tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subdir/subpage/page.tsx Adds a nested target page for subdir relative redirect assertions.
tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/subdir/page.tsx Adds client UI buttons to trigger server actions from a nested route.
tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/page.tsx Adds client UI buttons to trigger server actions from the root route.
tests/fixtures/app-basic/app/nextjs-compat/server-actions-relative-redirect/actions.ts Adds server actions that redirect via ./, ../, and absolute targets.
tests/e2e/app-router/nextjs-compat/server-actions-relative-redirect.spec.ts Adds Playwright regression coverage for Next.js-compatible relative redirect behavior and “no reload” expectation.
tests/app-browser-entry.test.ts Adds unit tests for redirect URL resolution + internal/external classification.
packages/vinext/src/server/app-browser-entry.ts Updates server action redirect handling to resolve dot-relative targets and use navigation runtime for same-origin redirects.
packages/vinext/src/server/app-browser-action-result.ts Introduces helper to resolve action redirect locations with Next.js directory-style dot-relative semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/vinext/src/server/app-browser-action-result.ts
Comment thread packages/vinext/src/server/app-browser-entry.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread packages/vinext/src/server/app-browser-entry.ts Outdated
@NathanDrake2406 NathanDrake2406 force-pushed the nathan/server-action-relative-redirect branch from 7e44827 to f148fb9 Compare May 23, 2026 06:58
Server action redirects could carry dot-relative locations from the action response. Treating those locations as raw browser redirects resolved them from the current document URL and forced a reload, so nested App Router actions could land on the wrong route or lose client state.

Resolve server action redirect locations against the action initiation pathname using Next.js directory-style semantics, keep same-origin redirects on the App Router navigation path, and hard-navigate only for external destinations or parse fallback.

Adds browser-level coverage ported from the Next.js relative server action redirect test plus focused helper assertions for the URL resolution contract.
@NathanDrake2406 NathanDrake2406 force-pushed the nathan/server-action-relative-redirect branch from f148fb9 to 68250cd Compare May 23, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants