Next.js Discord

Discord Forum

Server Components vs TanStack Query for Dashboard - Which Pattern for Next.js 16?

Unanswered
rob posted this in #help-forum
Open in Discord
robOP
I'm building a property management dashboard (Next.js 16.0.0, React 19, App Router) and trying to decide between two data fetching patterns.

Current Implementation (Server Components):

// app/(dashboard)/page.tsx
export const dynamic = 'force-dynamic'

export default async function Home() {
  const [stats, hasAIAccess] = await Promise.all([
    getDashboardStats(),
    canUseAIFeatures(),
  ])

  return (
    <DashboardOverview stats={stats}>
      <Suspense fallback={<Skeleton />}>
        <DeferredTenants />
      </Suspense>
    </DashboardOverview>
  )
}


Performance

* First load: ~650ms (Sydney → US East database)
* Uses React cache() for auth deduplication
* Zero client-side JS for data fetching

Considering Adding: TanStack Query for client-side caching

* Goal: Faster subsequent navigations (650ms → 100ms)
* Trade-off: Slower first load due to hydration (~1000ms)
* +40KB bundle size

Questions

* Is Server Components + Suspense the recommended pattern for dashboards in Next.js 16?
* Should I optimize for first load (Server Components) or navigation speed (TanStack Query)?
* Is there a hybrid approach where I use Server Components for initial data and TanStack Query for background updates?
* Are there Next.js 16 native caching features I'm missing that could help?

Context

* Data changes infrequently (user's tenants/payments, not real-time)
* Users don't rapidly navigate away/back from dashboard
* Already using Edge API routes with Cache-Control: private, s-maxage=30

Would love to hear what the recommended pattern is for this use case

10 Replies

Standard Chinchilla
Server Components are terrible for data fetching.

They completely fall apart once you try to handle errors or retries at a component level.

You can’t recover from errors atomically — error boundaries only catch the error, but you can’t remount a failed Server Component.

That means error recovery has to happen at the page level(error.tsx), not inside the component that fetched the data.

On top of that, if you use 'use-cache' with a short expiration(cacheLife({expire:3})), development becomes painful: Next.js recompiles every route on navigation, so you constantly hit rebuilds and blocked transitions.

And finally, there’s the artificial component separation. Since the error fallback must be a client component, you can’t colocate everything (data view, loading skeleton, and error state) in a single file. You end up splitting things just because of 'use client' boundaries — not because it makes sense architecturally.
And don’t even think about using Server Functions.

If you throw a custom error there, you’ll see your message in development — but in production, Next.js replaces it with a generic one.

They claim it’s “for security reasons,” apparently assuming you’re some careless kid who’d leak company secrets through error messages.

It’s a terrible developer experience. You lose all visibility into what failed, and it completely kills proper error handling in production
@rob I'm building a property management dashboard (Next.js 16.0.0, React 19, App Router) and trying to decide between two data fetching patterns. Current Implementation (Server Components): js // app/(dashboard)/page.tsx export const dynamic = 'force-dynamic' export default async function Home() { const [stats, hasAIAccess] = await Promise.all([ getDashboardStats(), canUseAIFeatures(), ]) return ( <DashboardOverview stats={stats}> <Suspense fallback={<Skeleton />}> <DeferredTenants /> </Suspense> </DashboardOverview> ) } Performance * First load: ~650ms (Sydney → US East database) * Uses React cache() for auth deduplication * Zero client-side JS for data fetching Considering Adding: TanStack Query for client-side caching * Goal: Faster subsequent navigations (650ms → 100ms) * Trade-off: Slower first load due to hydration (~1000ms) * +40KB bundle size Questions * Is Server Components + Suspense the recommended pattern for dashboards in Next.js 16? * Should I optimize for first load (Server Components) or navigation speed (TanStack Query)? * Is there a hybrid approach where I use Server Components for initial data and TanStack Query for background updates? * Are there Next.js 16 native caching features I'm missing that could help? Context * Data changes infrequently (user's tenants/payments, not real-time) * Users don't rapidly navigate away/back from dashboard * Already using Edge API routes with Cache-Control: private, s-maxage=30 Would love to hear what the recommended pattern is for this use case
Is Server Components + Suspense the recommended pattern for dashboards in Next.js 16?
Yep. Also with "use cache" or unstable_cache.

Should I optimize for first load (Server Components) or navigation speed (TanStack Query)?
You can do both in Next.js but it very much increases complexity since now you are either have to pick between tanstack's caching system or next's caching system or both.

Is there a hybrid approach where I use Server Components for initial data and TanStack Query for background updates?
There is hybrid approach, even TanStack Query made article on the docs on how to do this.

Are there Next.js 16 native caching features I'm missing that could help?
Unstable_cache or "use cache" or formally known as "Data Cache" is supposed to complement the fluid and seamless client-side navigation when not using any state mgmt libraries.

Users don't rapidly navigate away/back from dashboard
If your data changes in realtime, you might need more than what nextjs offers. Nextjs only handles data updates during navigations in and out of to. At best you can only do a "refresh" button that refreshes the whole thing and auto triggers in every few seconds. or maybe hook it to a on tab change handler (so data refreshes when user tabs into the page).

Next's SWR mechanisms makes it so that the page loads instantly while giving you slightly stale data while its revalidating (getting new data) in the background that makes it possible to have instant client-side navigation making smooth UI.

Tanstack query is generally usefull if you want to start doing client-side data fetching. Otherwise imo its completely doable without client-side data fetching as long as you properly cache your functions.
@Sun bear what is your stack <@413046679579918339> for building, you mentioned some good pain points i also have been encountering curios to see what you are you using.
Standard Chinchilla
I’m still using Next.js — I just stopped using Server Components for data fetching.
I’m doing everything with TanStack React Query again. It avoids all the RSC headaches and gives me proper retries, error recovery, and predictable behavior in both dev and production.

And honestly, the user-experience difference is almost imperceptible. You don’t gain enough to justify fighting all the RSC limitations. Most users won’t notice anything at all — maybe a tiny minority will, but for the average person the experience is basically the same.
Sun bear
yeah i found myself also using a mix of both
like react query
for pagination and caching that is beaufiful
since i dont want re-renders each time
like data can be fresh but not like on every request