Commit Graph

14 Commits

Author SHA1 Message Date
Sebastian "Sebbie" Silbermann 17f6f135e2 [fragment-scroll] Rename ScrollAndMaybeFocusHandler to ScrollHandler (#96828)
As of https://github.com/vercel/next.js/pull/95602, the old
scroll-and-focus handler doesn't exist anymore
As of https://github.com/vercel/next.js/pull/96113, the new handler no
longer handles focus.

This updates the relevant variable names to reflect that we no longer
handle focus.
2026-08-10 13:26:51 +02:00
Sebastian "Sebbie" Silbermann 2c04735944 [fragment-scroll] Remove config.experimental.appNewScrollHandler (#95602) 2026-08-06 09:53:09 +00:00
Sebastian "Sebbie" Silbermann f656c597c6 [fragment-scroll] Enable new scroll handler by default (#95378) 2026-07-06 18:39:07 +00:00
Hendrik Liebau 60e4f3061f Avoid re-logging server-originated errors forwarded from the browser (#94917)
During `next dev`, certain errors (such as Cache Components validation
errors) are logged directly to the terminal by the dev server and are
also sent to the browser so they can be shown in the dev overlay. The
browser logs them to its own console, and the browser-to-terminal log
forwarding would replay that copy back to the CLI, so the same error
appeared in the terminal twice.

This change marks those errors when the browser receives them, in the
`ERRORS_TO_SHOW_IN_BROWSER` handler, using a non-enumerable symbol.
`forwardErrorLog`, the single function all forwarded error logs pass
through, then skips any error carrying that marker, since it already
originated on the server and was logged there. The marker has to be set
browser-side because it wouldn't survive RSC serialization, which is the
same reason the error code is sent through a side-channel map rather
than on the error itself.

The error still shows in the browser console and the dev overlay; only
the redundant terminal echo is suppressed. This applies to every error
delivered over the errors RSC stream, all of which are logged on the
server before being sent, so the forwarded copy is always a duplicate.

> [!TIP]
> Review the two commits individually to see the before/after change.
2026-06-17 22:39:20 +02:00
Tim Neutkens e860cec656 test: migrate webdriver callers to next.browser (#93941)
### What?

Migrate remaining direct `next-webdriver` test callers that have a
`NextInstance` to `next.browser()`, and expose the shared `Playwright`
browser type from `e2e-utils`.

### Why?

`NextInstance.browser` should be the supported browser-opening interface
for test fixtures, with `next-webdriver` kept as the private
implementation detail.

### How?

Updated affected development, e2e, and production tests to call
`next.browser()` directly, passing `baseUrl` where tests intentionally
target a manually spawned or proxied server. Shared helpers now receive
browser callbacks from the test context, and browser types import
`Playwright` from `e2e-utils` instead of deriving from `next.browser` or
importing from private paths.

<!-- NEXT_JS_LLM_PR -->
2026-05-22 14:01:58 +02:00
Tim Neutkens 4588a73542 Convert tests using createNext -> nextTestSetup (#93767)
## What?

Converts existing `createNext()` usage into `nextTestSetup()`. 

`createNext()` was the setup step we had before `nextTestSetup()` was
added.

This PR focused on the simple conversion cases. There will be a
follow-up to complete the last few.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 13:16:31 +02:00
Andrew Clark 83e47acd77 Simplify scroll restoration with shared ScrollRef on CacheNode (#91348)
Replaces the segment-path-matching scroll system with a simpler model
based on a shared mutable ScrollRef on CacheNode.

The old system accumulated segment paths during navigation and matched
them in layout-router to decide which segments should scroll. This was
necessary when CacheNodes were created lazily during render. Now that we
construct the entire CacheNode tree immediately upon navigation, we can
assign a shared ScrollRef directly to each new leaf node. When any
segment scrolls, it flips the ref to false, preventing other segments
from also scrolling. This removes all the segment path accumulation and
matching logic.

Fixes a regression where calling `refresh()` from a server action
scrolled the page to the top. The old system had a semantic gap between
`null` (no segments) and `[]` (scroll everything) — a server action
refresh with no new segments fell through to a path that scrolled
unconditionally. The new model avoids this: refresh creates no new
CacheNodes, so no ScrollRef is assigned, and nothing scrolls.

Repro: https://github.com/stipsan/nextjs-refresh-regression-repro

There is extensive existing test coverage for scroll restoration
behavior. This adds one additional test for the server action refresh
bug.
2026-03-14 13:38:36 +01:00
Sebastian "Sebbie" Silbermann a8de0a0044 Enable experimental.reactDebugChannel by default (#90310)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-02-26 16:00:47 +01:00
Sebastian "Sebbie" Silbermann 5509e1bf7a [fragment-scroll] Stop focusing the first focusable host descendant (#89903)
Flagged behind `experimental.appNewScrollHandler`

`focus()` on Fragment refs works different than `findDOMNode(this).focus()`.

`findDOMNode` will return an actual host element so it's effectively `HTMLElement.focus()`. If the element isn't focuseable, `focus()` will do nothing. By spec, there's also no way to determine if an element is focusable. It's up to user agents (e.g. sometimes scrollable containers are considered focusable). 

`FragmentInstance.focus` will focus the first focusable descendant. That could be a focusable element deep within the fragment e.g. `<Fragment><section><p>A lot of text</p><a href="">Next page</a></Fragment>`. Focusing such a deep element may sometimes be counter productive.

Now we just blur the active element. This is how a hard navigation already works today and ensures we don't skip past content of the new segment.

This means you potentially stay a long way before the actual Segment (e.g. in a preceding nav) with the focus cursor which could be annoying. We could be clever and temporarily insert a focusable element at the start of the Segment, focus it, and then immediately remove. That would leave the focus cursor right before the Segment. However, this requires knowledge about the parent content (can we just insert an element of our choosing?) and relies on knowing what's focusable (a button would be a good bet). That's a bit too clever for a fix without concrete motivation so I'm waiting somebody to complain about the new behavior.
2026-02-18 09:50:39 -08:00
Sebastian "Sebbie" Silbermann 9662feb629 [fragment-scroll] Ensure hoisted elements don't break scroll-to-top on page navigations (#83108) 2026-02-14 14:42:31 +01:00
Jiachi Liu 488d90d141 [test] consolidate the browser log test (#89601)
Filter out the extra logging from test env so we only assert on the
snapshot of forwarded logs
```
Snapshot name: `Terminal Logging (Webpack) App Router - Hydration Errors should show hydration errors with owner stack trace 1`

- Snapshot  - 1
+ Received  + 9

@@ -39,7 +39,15 @@
     6 |     <div>
  >  7 |       <p>{isClient ? 'client' : 'server'}</p>
       |       ^
     8 |     </div>
     9 |   )
-   10 | }
+   10 | }
+
+
+ [12:36:48.104Z] Browser Log: received ws message {"type":"isrManifest","data":{"/hydration-error":true}}
+
+ [12:36:48.104Z] Browser Log: Next.js page already hydrated
+
+ [12:36:48.109Z] Hydration complete for http://localhost:46145/hydration-error
  "
    at Object.toMatchInlineSnapshot (/root/actions-runner/_work/next.js/next.js/test/development/browser-logs/browser-logs.test.ts:317:33)
    at runNextTicks (node:internal/process/task_queues:60:5)
    at processImmediate (node:internal/timers:449:9)
    at process.callbackTrampoline (node:internal/async_hooks:130:17)
```
2026-02-06 14:48:26 +01:00
Jiachi Liu 99c8038e33 Include owner stack for forwarded errors if available (#89493)
For forwarded logs, we can also attach the owner stack if it's
available, then we can see the proper errors trace shown in the
terminal.

In this way agent can directly detect where the error is happening from
reading terminal log, and fix it

| After | Before |
|:---|:---|
| <img width="937" height="778" alt="image"
src="https://github.com/user-attachments/assets/666cc012-4e79-41f4-9d6f-b9c1969316bf"
/> | <img width="942" height="641" alt="image"
src="https://github.com/user-attachments/assets/a44f795a-d297-4fb8-9efe-25e6e3684014"
/> |
2026-02-04 19:05:28 +01:00
Jiachi Liu 37ac3974db stabilize browser log forward options (#88857) 2026-01-22 12:11:53 +01:00
Rob Pruzan 94f8aecf26 feat: forward browser errors/logs to terminal (#80909)
Closes NEXT-4534

This PR introduces the ability for next.js to forward logs, errors, and
unhandled rejections from the browser to the terminal the dev server is
running in (behind an experimental flag)

# Explanation

The 2 main components of this pr are the client side error accumulation
logic, and the ingest handling on the other side of the hmr socket.

We listen on the existing hmr socket to send batched logs, errors, and
uncaught rejections the frame after they were captured. All forwarded
data is sent with metadata so we can have reconstruct the log with an
equivalent level of information to the browser- since we expect AI
agents that can't access the browsers to be consumers of this feature
(and it's generally useful).

All foreign data created by the user in the browser is serialized using
`safe-stable-serializer`, a popular serializer [used by other logging
libraries](https://www.npmjs.com/browse/depended/safe-stable-stringify),
like
[pino](https://github.com/search?q=repo%3Apinojs%2Fpino+safe-stable&type=code)
(safety, determinism, fast). We also have a light shim on top of json
serialization to handle displaying custom data representations that
either wouldn't survive serialization (undefined) or we want to present
to users in a custom format (throwing proxies, promises, ...)

On the dev server server, we (bespoke) deserialize, source map, format,
and log. I tried to share as much logic as I could with error dev
overlay to avoid feature drift since they are very similar
implementations other than the render target


# Explicitly covered cases

- console table
  - shows as `[browser]\n<table>\n(<source location>)`
- console trace
- shows as `[browser] arg1 arg2 ...\n<stack trace>\n([source mapped
location of log]`)`
  - trace is source mapped
- ignored frames are shown, incase people explicitly want the full trace
- console dir
  - shows as `[browser] arg1 arg2 ([source mapped location of log])`
- we need to explicitly capture stdout and rewrite it when we call nodes
`console.dir` to prefix and postfix with [browser] and and (`<source
mapped location of log>`) without adding newlines (we could do this for
console.table but it makes sense to keep the prefix and postfix on new
lines)

- console error
- `[browser] arg1 arg2 \n codeblock + source mapped stack of
console.error ([source mapped location of console.error])`
- if there are any `Error` values present we wont show the stack and
code block of console.error since it's overwhelming (this is fine since
we still tell the user where the `console.error` is with the appended
location)
- rejected promises that have `Error`'s
- behave identical to console.error, but is prepended with `⨯
unhandledRejection`
	- the `Error`'s render with their source mapped stack + ignored frames
- no error stack can be automatically appended where the promise
rejected
- rejected promises that have non `Error` values
- prepended with `⨯ unhandledRejection: ${error.name}: ${error.message}`
	- everything is logged in red
- no error stack can be automatically appended where the promise
rejected
- on caught error
	- prepended with `Uncaught ${errorName}: ${errorMessage}`
- stack attached to error is source mapped + ignored frames are not
shown
- everything is logged in red but the code block of where the error
orginated from
- all other console cases
  - shows as `[browser] arg1 arg2 ([source mapped location of log])`
- if an error is passed, we show the error name, message, source mapped
stack (colored white, ignored frames not shown), and code block if
available (syntax highlighted)
  - we apply util.format to handle formatted strings
- logs captured during RSC rendering
  - not piped to server, ignored on client


Closes NEXT-4534
2025-07-08 19:40:01 -04:00