Skip to content

next/form does not normalize newlines in GET submissions after hydration #98405

Description

@Tnalxmsk

Link to the code that reproduces this issue

https://github.com/Tnalxmsk/next-form-newline-repro

To Reproduce

  1. Clone the linked repository and open its directory.
  2. Run npm ci, then npm run build, followed by npm run start.
  3. Open http://localhost:3000/newline and wait for Hydrated: true.
  4. Leave the textarea unchanged and click Submit native. Inspect serverSearch.text on the result page.
  5. Return to /newline, wait for Hydrated: true, and click Submit next without changing the textarea.
  6. Compare serverSearch.text between the two submissions.

The same steps can also be run with npm run dev instead of the production build and start commands.

Both forms contain the same textarea with defaultValue={'line1\nline2'}. The submit buttons have no name or value attributes. No environment variables, external APIs, or account access are needed to run the reproduction.

Current vs. Expected behavior

Current behavior**

The native HTML form and hydrated next/form submit different strings for the same textarea content:

Submission Server-received text Encoded query value
Native HTML form "line1\r\nline2" line1%0D%0Aline2
Hydrated Next.js Form "line1\nline2" line1%0Aline2

The difference is present in the server's searchParams, not just the address bar. The single-line control value is preserved in both submissions.

Expected behavior

A string-action Next.js Form should normalize line endings to CRLF, matching a native HTML GET form.

The Next.js Form documentation describes string actions as behaving like native GET forms. The HTML form submission algorithm normalizes standalone CR and LF characters in field names and values to CRLF before URL encoding.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.5.0: Mon Apr 27 20:40:51 PDT 2026; root:xnu-12377.121.6~2/RELEASE_ARM64_T8112
  Available memory (MB): 16384
  Available CPU cores: 8
Binaries:
  Node: 24.2.0
  npm: 11.3.0
  Yarn: 1.22.22
  pnpm: 11.24.0
Relevant Packages:
  next: 16.4.0-canary.22 // Latest available version is detected (16.4.0-canary.22).
  eslint-config-next: N/A
  react: 19.2.1
  react-dom: 19.2.1
  typescript: N/A
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Form (next/form)

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local)

Additional context

  • Previous report: #98404. It was automatically closed after the reproduction checker received HTTP 403 from the CodeSandbox URL. This report uses a public GitHub reproduction repository instead.
  • Locally reproduced with Next.js 16.3.4 and 16.4.0-canary.22, React/ReactDOM 19.2.1, and Node 24.2.0, in both development and production. Both production builds succeeded.
  • The optional CodeSandbox preview was separately verified in production with Next.js 16.4.0-canary.22, React/ReactDOM 19.2.1, and Node 20.12.0.
  • Browser used for verification: Chromium-based ego-browser on macOS, with a Chrome/150.0.0.0 user agent.
  • In a separate local production check, blocking /_next/static/* requests and confirming Hydrated: false before submission caused Next.js Form to submit CRLF, like the native form. The LF result occurs after hydration.
  • The relevant implementation creates new FormData(formElement) and then appends the entries directly to URLSearchParams, without normalizing line endings: form-shared.tsx.
  • The first affected release has not been identified.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions