Skip to content

Conversation

gruckion
Copy link

Changes

data-table-filters/src/app/infinite/

  1. Fixed prefetchInfiniteQuery usage by wrapping the table in <HydrationBoundary>. Otherwise prefetching will not occur.
  2. Added staleTime with non-zero value to the dataOptions to prevent immediate refetching.
  3. Fixed Client component to accept server-parsed search params as props to avoid re-parsing on the client and timestamp mismatches.
  4. Fixed initialPageParam in query options to use search.cursor.getTime() instead of new Date().getTime() to ensure the pageParam matches between server prefetch and client hydration.
  5. Added loading.tsx and Skeleton component for better loading states during navigation (following the pattern from default table).

data-table-filters/src/app/light/

  1. Added staleTime with non-zero value to the dataOptions to prevent immediate refetching.
  2. Wrapped the table in <HydrationBoundary> for proper hydration.

data-table-filters/src/app/default/

Refactored to be consistent with the infinite scroll example, with async data fetching via the API route and non-zero staleTime for prefetching to not immediately refetch.

  1. Refactored the DataTable into it's own Client component for consistency with the infinite scroll example.
  2. Refactored the data to be async and fetch via the API route.
  3. Created dataOptions to fetch the data via the API route with non-zero staleTime for prefetching to not immediately refetch.
  4. Removed React.Suspense in favour of Next.js loading.tsx Alex Sidorenko "Navigation feels slow in Next.js".
  5. Wrapped the table in a HydrationBoundary otherwise prefetching will not occur.

fix: add HydrationBoundary and fix prefetch for data tables  - Wrap all data tables in HydrationBoundary for proper SSR hydration - Add non-zero staleTime to prevent immediate client-side refetching - Fix infinite query initialPageParam to use stable cursor value - Pass server-parsed search params to client to avoid timestamp mismatches - Add loading states for better navigation experience  Fixes prefetchQuery/prefetchInfiniteQuery not working without proper hydration setup  The commit follows conventional commits format: - Type: fix (this is a bug fix - prefetch wasn't working) - Scope: Could optionally add scope like fix(data-table): if you prefer - Description: Clear and concise - Body: Lists the key changes - Footer: Explains what issue it fixes
Copy link

vercel bot commented Sep 21, 2025

@gruckion is attempting to deploy a commit to the OpenStatus Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

@franky47 franky47 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue with using Date parsers in nuqs is that they all (server and client) have to be rooted in UTC to be consistent, and for the URL to have the same semantic meaning across the world.

This can (depending on your side of the UTC-0 line) be a bit jarring: parseAsIsoDate.parse('2025-09-22') gives you the point in time at midnight, on the 22nd of September 2025 in UTC, so if you happen to be, say, in UTC+2 (like me as I write this), when you select a date from a UI set for local time, it actually renders the day before in the URL.

Timestamps indeed don't have this problem, both because they are explicitly rooted in UTC, and because they avoid human scrutiny.

queryKey: ["default-data", search],
queryFn: async () => {
// Use absolute URL for server-side fetching
const baseUrl = typeof window === 'undefined'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: could you import the data directly here when in server-side (à la RSC), rather than going through HTTP server -> server? Or does it pose bundling separation issues?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with you @franky47 - if it's a production app, I'd do a db query straight to get the data. Here, to keep the data consistent and change it only in a single place, I'd suggest to leave the http call.

@gruckion I think we would need to use the https://${VERCEL_URL} on non dev environments to make it work.

we should probably do the same for the /infinite/query-options to make it fetch from the server.

Copy link

vercel bot commented Sep 21, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
data-table-filters Ready Ready Preview Comment Sep 21, 2025 6:39pm

@franky47
Copy link

The time range selectors don't work.

On the default page:

  • Selecting a single date gives you a range in the URL that's {midnightUTCForThatDay}-{midnighUTCForThatDay}, it should probably be {midnightUTCForThatDay}-{23:59UTCForThatDay}
  • The data shows log points for today (2025-09-21), but selecting from the 20th to the 22nd doesn't include them.

On the infinite page, the selection works from the tooltip, but now when dragging from the bars on top of the table.

Copy link
Member

@mxkaske mxkaske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @gruckion! appreciate it a lot. ❤️

While the default table works, I had issues with the infinite table. Left some comments while trying to debug it.

json,
);
},
initialPageParam: { cursor: new Date().getTime(), direction: "next" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

queryKey: ["default-data", search],
queryFn: async () => {
// Use absolute URL for server-side fetching
const baseUrl = typeof window === 'undefined'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with you @franky47 - if it's a production app, I'd do a db query straight to get the data. Here, to keep the data consistent and change it only in a single place, I'd suggest to leave the http call.

@gruckion I think we would need to use the https://${VERCEL_URL} on non dev environments to make it work.

we should probably do the same for the /infinite/query-options to make it fetch from the server.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the HydrationBoundary doesn't seem to work on the infinite page for me.

when adding the absolute url to the query-options - the isLoading property from useInfiniteQuery always starts to be true - which shouldn't be the case.

I'm trying to debug it currently the dataOptions(search) returns two different values based on server and client.

const search = searchParamsCache.parse(await searchParams);
const search = await searchParamsCache.parse(searchParams);
const queryClient = getQueryClient();
await queryClient.prefetchInfiniteQuery(dataOptions(search));
Copy link
Member

@mxkaske mxkaske Sep 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when logging on server component (/infinite/page.tsx)

  const queryOptions = dataOptions(search);
  console.log('server key', queryOptions); // [ 'data-table', [ 'data-table', '' ] ]
  await queryClient.prefetchInfiniteQuery(queryOptions);

while in client component (/infinite/client.tsx)

const queryOptions = dataOptions(search);
console.log('client key', queryOptions); // [ 'data-table', '?cursor=1758483849134' ]
const { .. } = useInfiniteQuery(queryOptions);

while search is exactly same!

To be exact, the searchParamsSerializer = createSerializer(searchParamsParser) returns two different values based on server and client.

@franky47 is that known?

Copy link

@franky47 franky47 Sep 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the problem is in the definition of the default value for cursor in the search params definitions object: https://github.com/gruckion/data-table-filters/blob/3b564725b96f53d093567466eab64530f367f732/src/app/infinite/search-params.ts#L58

If you log the timestamp for this new Date(), you'll see it differs between the client and the server, which makes sense because the code is evaluated at different times.

So on the server, the serializer sees the default value as equal (comparison by timestamp), but on the client it doesn't, and therefore renders it to the URL, giving you different React Query keys.

@mxkaske
Copy link
Member

mxkaske commented Sep 21, 2025

On the default page:

  • Selecting a single date gives you a range in the URL that's {midnightUTCForThatDay}-{midnighUTCForThatDay}, it should probably be {midnightUTCForThatDay}-{23:59UTCForThatDay}
  • The data shows log points for today (2025-09-21), but selecting from the 20th to the 22nd doesn't include them.

On the infinite page, the selection works from the tooltip, but now when dragging from the bars on top of the table.

potentially non-related issues to the code changes 😅 - at least selecting a single data doesn't seem to work on prod

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants