Skip to content

HMR: debounce status indicator transitions to prevent flicker during bursts of changes#93229

Open
wbinnssmith wants to merge 1 commit intocanaryfrom
wbinnssmith/debounce-devtool-hmr-ui
Open

HMR: debounce status indicator transitions to prevent flicker during bursts of changes#93229
wbinnssmith wants to merge 1 commit intocanaryfrom
wbinnssmith/debounce-devtool-hmr-ui

Conversation

@wbinnssmith
Copy link
Copy Markdown
Member

@wbinnssmith wbinnssmith commented Apr 24, 2026

This adds a debounce to the ui in development, reducing churn and flicker from the Next.js logo indicator's "Compiling" and "Rendering" messages. This is wholistic and covers any rapid changes in dev, usually caused by agents making a series of changes.

There's currently a debounce of 30ms coming from the subscription to changes in Turbopack itself, we could consider lengthening that as well.

This commit adds a useDebouncedValue hook and uses it to debounce status transitions in NextLogo, so rapid active->active alternations (e.g. Compiling→Rendering→Compiling) are smoothed into a single stable state for 300ms. Transitions to/from None remain immediate so:

  • Fast single builds that complete before the 400ms enterDelay still never show the pill.
  • The pill still appears promptly when a long build starts (no added latency on top of the existing enter delay).

Changes:

  • use-debounced-value.ts: new generic trailing-edge debounce hook with an optional leading predicate to bypass the delay for specific transitions.
  • next-logo.tsx: apply the debounce to the computed status; derive shouldShowStatus from the debounced value so both pill mount-gating and
    label transitions are smoothed together.

Test Plan: Ran a script that simulated an agent making hundreds of changes in rapid successions. Verified before this change, the indicator would flash between Compiling and Rendering rapidly. Now, it consistently stays on "Compiling" before the final render.

…bursts of changes

This adds a debounce to the _ui_ in development, reducing churn and flicker from the Next.js logo indicator's "Compiling" and "Rendering" messages. This is wholistic and covers any rapid changes in dev, usually caused by agents making a series of changes.

This commit adds a useDebouncedValue hook and uses it to debounce status transitions in NextLogo, so rapid active->active alternations (e.g.  Compiling→Rendering→Compiling) are smoothed into a single stable state for 300ms. Transitions to/from None remain immediate so:

- Fast single builds that complete before the 400ms enterDelay still never show the pill.
- The pill still appears promptly when a long build starts (no added latency on top of the existing enter delay).

Changes:
- use-debounced-value.ts: new generic trailing-edge debounce hook with an optional leading predicate to bypass the delay for specific transitions.
- next-logo.tsx: apply the debounce to the computed status; derive shouldShowStatus from the debounced value so both pill mount-gating and
  label transitions are smoothed together.

Test Plan: Ran a script that simulated an agent making hundreds of changes in rapid successions. Verified before this change, the indicator would flash between Compiling and Rendering rapidly. Now, it consistently stays on "Compiling" before the final render.
@github-actions github-actions Bot added created-by: Turbopack team PRs by the Turbopack team. type: next labels Apr 24, 2026
@wbinnssmith wbinnssmith marked this pull request as ready for review April 25, 2026 00:04
@github-actions
Copy link
Copy Markdown
Contributor

Failing test suites

Commit: ad17296 | About building and testing Next.js

pnpm test-start test/e2e/app-dir/concurrent-navigations/mismatching-prefetch.test.ts (job)

  • mismatching prefetch > recovers when a navigation rewrites to a different route than the one that was prefetched (DD)
Expand output

● mismatching prefetch › recovers when a navigation rewrites to a different route than the one that was prefetched

thrown: "Exceeded timeout of 60000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  50 |       }
  51 |
> 52 |       const result = Reflect.apply(target, thisArg, args)
     |                              ^
  53 |       return typeof result === 'function' ? wrapJestTestFn(result) : result
  54 |     },
  55 |     get(target, prop, receiver) {

  at Object.apply (lib/e2e-utils/index.ts:52:30)
  at it (e2e/app-dir/concurrent-navigations/mismatching-prefetch.test.ts:19:3)
  at Object.describe (e2e/app-dir/concurrent-navigations/mismatching-prefetch.test.ts:5:1)

@github-actions
Copy link
Copy Markdown
Contributor

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change
Cold (Listen) 811ms 812ms
Cold (Ready in log) 784ms 783ms
Cold (First Request) 1.249s 1.237s
Warm (Listen) 811ms 811ms
Warm (Ready in log) 783ms 784ms
Warm (First Request) 597ms 598ms
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change
Cold (Listen) 811ms 810ms
Cold (Ready in log) 785ms 779ms
Cold (First Request) 3.116s 3.118s
Warm (Listen) 810ms 810ms
Warm (Ready in log) 783ms 776ms
Warm (First Request) 3.124s 3.123s

⚡ Production Builds

Metric Canary PR Change
Fresh Build 4.928s 4.838s
Cached Build 4.882s 4.847s
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change
Fresh Build 23.308s 23.270s
Cached Build 23.267s 23.294s
node_modules Size 495 MB 495 MB
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
07rxhp_1_g4mu.js gzip 13.1 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0fli3_wppnim5.js gzip 12.9 kB N/A -
0jaojkrb2rf_l.js gzip 158 B N/A -
0k09jwjeb-tki.js gzip 13.8 kB N/A -
0kb7_ep3r1z0_.js gzip 10.1 kB N/A -
0kw8xgqdrilf6.js gzip 8.56 kB N/A -
0ojkk2e654xsc.js gzip 8.59 kB N/A -
0wxpyd8r-vipl.js gzip 1.47 kB N/A -
0xy2fhla48_rd.js gzip 9.24 kB N/A -
0zg1_op6gpm77.js gzip 159 B N/A -
10u1wg6830n69.js gzip 155 B N/A -
10wqsvi2mgfmi.js gzip 9.82 kB N/A -
16lhqjoqbznyg.js gzip 220 B 220 B
16vepdkipri3r.js gzip 8.51 kB N/A -
17n96uu6y1pxq.js gzip 8.6 kB N/A -
18y4_8-9or0mn.js gzip 8.51 kB N/A -
1ejk4yy877m2s.js gzip 156 B N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1gq145j3kps-h.js gzip 8.62 kB N/A -
1nsh-mbn0e-se.js gzip 8.56 kB N/A -
1o8iht4q821x5.js gzip 49.4 kB N/A -
1tsrrp1tdngti.js gzip 13.3 kB N/A -
1wbblhm8kdg1d.js gzip 70.9 kB N/A -
2__-e_ym8n788.js gzip 450 B N/A -
21h2q5l_ijxo1.js gzip 156 B N/A -
22o6xd9_ywdu6.js gzip 233 B N/A -
25ee1c0k3yry7.js gzip 155 B N/A -
25n272-g99oa1.js gzip 7.61 kB N/A -
2jqyc54izxg_5.js gzip 154 B N/A -
2kvj8yrfznmwx.js gzip 5.69 kB N/A -
2m-x8jeelz1qx.js gzip 169 B N/A -
2qv7m7xjnokgr.js gzip 8.58 kB N/A -
2t9oc38of5_ar.js gzip 153 B N/A -
2zf1ecbz94sp-.js gzip 65.5 kB N/A -
342ijzvrpe53h.js gzip 2.29 kB N/A -
3b0qzjz24jcjn.js gzip 157 B N/A -
3k1k5gtofm6eq.js gzip 10.4 kB N/A -
3m606uqf8ws_m.js gzip 155 B N/A -
3msaeh9eztxfm.js gzip 161 B N/A -
3pgbrgq66slx4.js gzip 155 B N/A -
turbopack-02..a6-3.js gzip 4.19 kB N/A -
turbopack-0b..7ght.js gzip 4.17 kB N/A -
turbopack-19..b8v3.js gzip 4.19 kB N/A -
turbopack-1k..fy7t.js gzip 4.19 kB N/A -
turbopack-1n..nqqb.js gzip 4.2 kB N/A -
turbopack-1z..cxi_.js gzip 4.19 kB N/A -
turbopack-21..uim9.js gzip 4.19 kB N/A -
turbopack-27..jjpo.js gzip 4.19 kB N/A -
turbopack-2m..i5vn.js gzip 4.19 kB N/A -
turbopack-2m..1fe1.js gzip 4.19 kB N/A -
turbopack-2v..1576.js gzip 4.19 kB N/A -
turbopack-33..fmta.js gzip 4.19 kB N/A -
turbopack-3q..h-7b.js gzip 4.19 kB N/A -
turbopack-3u..fo-_.js gzip 4.19 kB N/A -
01ofzfnqdbuen.js gzip N/A 158 B -
0arkbdqpxc37i.js gzip N/A 8.6 kB -
0bz-xifewa17d.js gzip N/A 8.63 kB -
0gh_yv8qw4m5-.js gzip N/A 157 B -
0im0h0br03kar.js gzip N/A 156 B -
0tvekitj587fh.js gzip N/A 8.51 kB -
0yvk6-wi8e9wh.js gzip N/A 13.3 kB -
1-jqyfc89tixo.js gzip N/A 1.46 kB -
10y3h86mnhs_2.js gzip N/A 10.4 kB -
14t1kneseb8th.js gzip N/A 2.3 kB -
15sb1-dsqfk_j.js gzip N/A 8.59 kB -
1ab2xruymo-oj.js gzip N/A 449 B -
1cz0kmzuvr895.js gzip N/A 70.9 kB -
1tu25qtsmfhar.js gzip N/A 9.82 kB -
1vein_gnv3mwr.js gzip N/A 8.56 kB -
1wzrm0xjjbzn5.js gzip N/A 10.1 kB -
1z3g0uaqtv9_3.js gzip N/A 8.56 kB -
25a1yz7zua29z.js gzip N/A 13.8 kB -
2auzvn7t9xf96.js gzip N/A 65.5 kB -
2bi5hx402juv-.js gzip N/A 8.58 kB -
2fiqb4-ar30kx.js gzip N/A 49.4 kB -
2fuln2dxq8d_0.js gzip N/A 154 B -
2hy56297fog9u.js gzip N/A 8.52 kB -
2qd9d0wg7yxwc.js gzip N/A 158 B -
2u_rpxq3tzytl.js gzip N/A 233 B -
2wr55o64ssudv.js gzip N/A 161 B -
2yy2v4vukl6e0.js gzip N/A 161 B -
35-eg4zotgxro.js gzip N/A 157 B -
35nh2lh_i5pyh.js gzip N/A 7.61 kB -
368lim5wq0o0r.js gzip N/A 12.9 kB -
3drqjohogojbw.js gzip N/A 5.69 kB -
3g8l1m2-o-ewi.js gzip N/A 13.1 kB -
3ixmpqnyxmqfs.js gzip N/A 170 B -
3k0wlheipb1ej.js gzip N/A 159 B -
3km28rtkbqo-g.js gzip N/A 158 B -
3n9xy43ds2l11.js gzip N/A 159 B -
3tfgis6xa1unl.js gzip N/A 158 B -
3wpp8nvyoj121.js gzip N/A 9.24 kB -
turbopack-0-..xakf.js gzip N/A 4.19 kB -
turbopack-09..ikej.js gzip N/A 4.19 kB -
turbopack-0j..nybk.js gzip N/A 4.19 kB -
turbopack-0o..1pwm.js gzip N/A 4.21 kB -
turbopack-1m..wh9e.js gzip N/A 4.17 kB -
turbopack-1r..xsvl.js gzip N/A 4.19 kB -
turbopack-1t..xctc.js gzip N/A 4.19 kB -
turbopack-28..ffdj.js gzip N/A 4.19 kB -
turbopack-2k..ao0y.js gzip N/A 4.19 kB -
turbopack-2o..i1c7.js gzip N/A 4.19 kB -
turbopack-2o..wsn2.js gzip N/A 4.19 kB -
turbopack-2t..clq6.js gzip N/A 4.19 kB -
turbopack-35..-wtq.js gzip N/A 4.19 kB -
turbopack-3w..fwa0.js gzip N/A 4.19 kB -
Total 465 kB 465 kB ⚠️ +90 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 721 B 719 B
Total 721 B 719 B ✅ -2 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 435 B 433 B
Total 435 B 433 B ✅ -2 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2637-HASH.js gzip 4.63 kB N/A -
7724.HASH.js gzip 169 B N/A -
8274-HASH.js gzip 61.3 kB N/A -
8817-HASH.js gzip 5.59 kB N/A -
c3500254-HASH.js gzip 62.8 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 254 B 255 B
main-HASH.js gzip 39.4 kB 39.3 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
5887-HASH.js gzip N/A 5.61 kB -
6522-HASH.js gzip N/A 60.6 kB -
6779-HASH.js gzip N/A 4.63 kB -
8854.HASH.js gzip N/A 169 B -
eab920f9-HASH.js gzip N/A 62.8 kB -
Total 235 kB 235 kB ✅ -619 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 193 B 193 B
_error-HASH.js gzip 182 B 182 B
css-HASH.js gzip 333 B 334 B
dynamic-HASH.js gzip 1.81 kB 1.8 kB
edge-ssr-HASH.js gzip 255 B 255 B
head-HASH.js gzip 353 B 349 B 🟢 4 B (-1%)
hooks-HASH.js gzip 384 B 382 B
image-HASH.js gzip 581 B 581 B
index-HASH.js gzip 260 B 259 B
link-HASH.js gzip 2.52 kB 2.52 kB
routerDirect..HASH.js gzip 316 B 318 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 313 B 314 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.99 kB 7.98 kB ✅ -10 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 126 kB 126 kB
page.js gzip 274 kB 273 kB
Total 400 kB 399 kB ✅ -619 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 617 B 616 B
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 44.4 kB 44.3 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 46 kB 45.9 kB ✅ -59 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 722 B 719 B
Total 722 B 719 B ✅ -3 B
Build Cache
Canary PR Change
0.pack gzip 4.39 MB 4.39 MB
index.pack gzip 114 kB 113 kB
index.pack.old gzip 115 kB 115 kB
Total 4.62 MB 4.62 MB ✅ -5.39 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 348 kB 348 kB
app-page-exp..prod.js gzip 193 kB 193 kB
app-page-tur...dev.js gzip 347 kB 347 kB
app-page-tur..prod.js gzip 193 kB 193 kB
app-page-tur...dev.js gzip 344 kB 344 kB
app-page-tur..prod.js gzip 191 kB 191 kB
app-page.run...dev.js gzip 344 kB 344 kB
app-page.run..prod.js gzip 191 kB 191 kB
app-route-ex...dev.js gzip 77.3 kB 77.3 kB
app-route-ex..prod.js gzip 52.7 kB 52.7 kB
app-route-tu...dev.js gzip 77.3 kB 77.3 kB
app-route-tu..prod.js gzip 52.8 kB 52.8 kB
app-route-tu...dev.js gzip 76.9 kB 76.9 kB
app-route-tu..prod.js gzip 52.5 kB 52.5 kB
app-route.ru...dev.js gzip 76.9 kB 76.9 kB
app-route.ru..prod.js gzip 52.5 kB 52.5 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 44.2 kB 44.2 kB
pages-api-tu..prod.js gzip 33.6 kB 33.6 kB
pages-api.ru...dev.js gzip 44.2 kB 44.2 kB
pages-api.ru..prod.js gzip 33.6 kB 33.6 kB
pages-turbo....dev.js gzip 53.5 kB 53.5 kB
pages-turbo...prod.js gzip 39.3 kB 39.3 kB
pages.runtim...dev.js gzip 53.5 kB 53.5 kB
pages.runtim..prod.js gzip 39.3 kB 39.3 kB
server.runti..prod.js gzip 63.1 kB 63.1 kB
Total 3.07 MB 3.07 MB ⚠️ +1 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/ad1729641e5b3aad56b5c2560d15bf338432bb21/next

Commit: ad17296

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants