@@ -90,11 +90,21 @@ export default function SeriesTable({
9090 ) ;
9191 const [ searchInput , setSearchInput ] = useState ( search ) ;
9292
93- // Sync URL-driven search changes (e.g. browser back/forward) into the input
93+ // Local page state for immediate UI response. The nuqs `setParams` update
94+ // goes through `startTransition` inside the React Router adapter, which can
95+ // silently defer the state commit in React 19 production builds. By keeping
96+ // a direct React state we guarantee the table re-renders on page change.
97+ const [ localPage , setLocalPage ] = useState ( params . page ) ;
98+
99+ // Sync URL-driven changes (e.g. browser back/forward) into local state
94100 useEffect ( ( ) => {
95101 setSearchInput ( search ) ;
96102 } , [ search ] ) ;
97103
104+ useEffect ( ( ) => {
105+ setLocalPage ( params . page ) ;
106+ } , [ params . page ] ) ;
107+
98108 const [ rowSelection , setRowSelection ] = useState < RowSelectionState > ( { } ) ;
99109 const [ confirmDelete , setConfirmDelete ] = useState ( false ) ;
100110
@@ -166,7 +176,7 @@ export default function SeriesTable({
166176 0 ,
167177 Math . ceil ( filteredData . length / pageSize ) - 1 ,
168178 ) ;
169- const effectivePage = Math . min ( Math . max ( params . page , 0 ) , maxPageEstimate ) ;
179+ const effectivePage = Math . min ( Math . max ( localPage , 0 ) , maxPageEstimate ) ;
170180
171181 const pagination = useMemo (
172182 ( ) => ( { pageIndex : effectivePage , pageSize } ) ,
@@ -318,6 +328,7 @@ export default function SeriesTable({
318328 typeof updaterOrValue === "function"
319329 ? updaterOrValue ( sorting )
320330 : updaterOrValue ;
331+ setLocalPage ( 0 ) ;
321332 setParams ( {
322333 sort : newSorting . length > 0 ? newSorting [ 0 ] : null ,
323334 page : null ,
@@ -330,6 +341,7 @@ export default function SeriesTable({
330341 : updaterOrValue ;
331342 const newPage = newPagination . pageIndex ;
332343 if ( newPage !== effectivePage ) {
344+ setLocalPage ( newPage ) ;
333345 setParams ( { page : newPage === 0 ? null : newPage } ) ;
334346 }
335347 } ,
@@ -347,19 +359,17 @@ export default function SeriesTable({
347359
348360 const pageCount = table . getPageCount ( ) ;
349361
350- // Sync URL when page is out of bounds (e.g. search filter reduced results).
351- // The table already renders the clamped effectivePage, so this only fixes
352- // the URL — it is not on the critical render path.
362+ // Clamp page when out of bounds (e.g. search filter reduced results).
353363 useEffect ( ( ) => {
354364 const maxPage = Math . max ( 0 , pageCount - 1 ) ;
355- const clampedPage = Math . min ( Math . max ( params . page , 0 ) , maxPage ) ;
365+ const clampedPage = Math . min ( Math . max ( localPage , 0 ) , maxPage ) ;
356366
357- if ( clampedPage !== params . page ) {
367+ if ( clampedPage !== localPage ) {
368+ setLocalPage ( clampedPage ) ;
358369 setParams ( { page : clampedPage === 0 ? null : clampedPage } ) ;
359370 }
360- // setParams is a stable setter from useQueryStates — safe to omit
361371 // eslint-disable-next-line react-hooks/exhaustive-deps
362- } , [ pageCount , params . page ] ) ;
372+ } , [ pageCount , localPage ] ) ;
363373
364374 if ( isLoading ) {
365375 return (
@@ -395,24 +405,27 @@ export default function SeriesTable({
395405 typeFilter = { typeFilter }
396406 progressFilter = { progressFilter }
397407 statusFilter = { statusFilter }
398- onTypeChange = { ( value ) =>
408+ onTypeChange = { ( value ) => {
409+ setLocalPage ( 0 ) ;
399410 setParams ( {
400411 type : value === "all" ? null : value ,
401412 page : null ,
402- } )
403- }
404- onProgressChange = { ( value ) =>
413+ } ) ;
414+ } }
415+ onProgressChange = { ( value ) => {
416+ setLocalPage ( 0 ) ;
405417 setParams ( {
406418 progress : value === "all" ? null : value ,
407419 page : null ,
408- } )
409- }
410- onStatusChange = { ( value ) =>
420+ } ) ;
421+ } }
422+ onStatusChange = { ( value ) => {
423+ setLocalPage ( 0 ) ;
411424 setParams ( {
412425 status : value === "all" ? null : value ,
413426 page : null ,
414- } )
415- }
427+ } ) ;
428+ } }
416429 counts = { filterCounts }
417430 />
418431 < div className = "flex items-center gap-2" >
@@ -437,6 +450,7 @@ export default function SeriesTable({
437450 variant = "ghost"
438451 size = "sm"
439452 onClick = { ( ) => {
453+ setLocalPage ( 0 ) ;
440454 setParams ( { view : "grid" , page : null } ) ;
441455 setRowSelection ( { } ) ;
442456 } }
@@ -456,6 +470,7 @@ export default function SeriesTable({
456470 onChange = { ( e ) => {
457471 setSearchInput ( e . target . value ) ;
458472 setSearch ( e . target . value || null ) ;
473+ setLocalPage ( 0 ) ;
459474 setParams ( { page : null } ) ;
460475 } }
461476 className = "w-[200px]"
0 commit comments