Skip to content

icyJoseph/next-post-form-proxy

Repository files navigation

body-work-around

Exploration of workarounds for vercel/next.js#92919 — "I need a way to read POST body on a page route." Two paths are implemented; both preserve step-to-step state without cookies, localStorage, query params, or hidden client state machines.

1. Proxy POST flow (/form)

Works with JavaScript disabled. Native browser form POST → route.ts extracts the body → internally fetches a sibling page with the body forwarded in a header → streams HTML back. The browser sees a standard POST/response pair; URL bar reads /form/confirm.

app/form/page.tsx               # email form, action="/form/confirm"
app/form/confirm/route.ts       # POST: read body → fetch view → return Response
                                # GET:  redirect to /form
app/form/confirm/view/page.tsx  # reads header via next/headers; HMAC-gated
app/form/confirm/proxy-token.ts # sign() / verify() — short-lived HMAC
app/form/verify/route.ts        # OTP verify; success redirects, failure re-renders view

Why confirm/view/ and not confirm/? route.ts and page.tsx cannot share a URL segment. The HMAC proxy token stops direct hits to the internal view.

Tradeoffs: extra internal HTTP hop per submit; body has to fit in a header (~8KB ceiling on most servers).

2. Server-action UI flow (/form-ui)

Requires JavaScript. Server action receives FormData, returns the JSX of the next step (an RSC fragment). useActionState replaces the form contents with whatever the server returned. No URL change, no page swap, no cookies.

app/form-ui/page.tsx     # server component, mounts <Flow initialUi={<EmailStep />} />
app/form-ui/flow.tsx     # 'use client' — useActionState(stepAction, initialUi)
app/form-ui/actions.tsx  # 'use server' — returns <EmailStep /> or <OtpStep /> based on formData
app/form-ui/steps.tsx    # step components (form contents only, no <form> wrapper)

The returned JSX is the state. Inter-step data travels via hidden inputs inside the returned fragment.

Tradeoffs: needs JS; single history entry (no back-button per step).

Side-by-side

Proxy POST (/form) Server-action UI (/form-ui)
Works without JS yes no
URL changes per step yes no
History entry per step yes no
Server state none none
Extra server hop one internal fetch none

Running

pnpm install
pnpm dev

Then open http://localhost:3000. Token 0000 is the only valid OTP.

Set FORM_PROXY_SECRET in production — the HMAC falls back to a dev-only constant otherwise.

About

Exploring workarounds for POST body on page routes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors