Belajar TanStack Query - SSR & Framework Integration
Episode 14 of 23

Belajar TanStack Query - SSR & Framework Integration

Episode ini membahas integrasi server-side rendering: dehydrate dan hydrate dengan HydrationBoundary untuk Next.js App Router, prefetch di server components, serta integrasi dengan Remix loaders dan pola framework-agnostic memakai query core.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

Pada SSR (server-side rendering), halaman dirender di server lalu dikirim sebagai HTML. Masalahnya: kalau data hanya di-fetch di client, HTML awal kosong dan user melihat loading. Solusi TanStack Query: dehydrate cache di server, kirim serialisasinya bersama HTML, lalu hydrate di client supaya halaman langsung penuh tanpa fetch ulang.

Episode 14 membahas pola hydration untuk Next.js App Router dan Remix, ditambah cara memakai query core di luar framework.

Next.js App Router

Peran dehydrate dan hydrate

Dua fungsi kunci: dehydrate membekukan cache menjadi serializable state, dan hydrate memulihkannya di sisi client. Keduanya dihubungkan oleh HydrationBoundary:

Alur hydration SSR
server: prefetch → dehydrate → kirim state serialized
client: hydrate state → render instan dari cache

Diagram server: prefetch dehydrate menampilkan langkah di server, dan client: hydrate state langkah di client. HydrationBoundary adalah jembatan antar keduanya.

Prefetch di Server Component

Di App Router, server components bisa prefetch ke cache yang dibuat di server. TanStack Query menyediakan pola helper getQueryClient untuk membungkus satu instance per request:

JSHelper getQueryClient
import { QueryClient, isServer } from "@tanstack/react-query"
 
function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,
      },
    },
  })
}
 
let browserQueryClient = undefined
 
export function getQueryClient() {
  if (isServer) {
    return makeQueryClient()
  }
  if (!browserQueryClient) {
    browserQueryClient = makeQueryClient()
  }
  return browserQueryClient
}

getQueryClient mengembalikan instance baru di server (per request) dan instance yang sama di browser (per sesi). isServer membedakan konteks runtime. Ini mencegah cache bocor antar request di server dan cache berganda di client.

Server Component dengan HydrationBoundary

Dalam server component, prefetch lalu bungkus dengan HydrationBoundary yang menerima cache yang di-dehydrate:

JSPrefetch dan hydrate di App Router
import { dehydrate, HydrationBoundary } from "@tanstack/react-query"
import { getQueryClient } from "./get-query-client"
 
export default async function TodosPage() {
  const queryClient = getQueryClient()
 
  await queryClient.prefetchQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
  })
 
  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Todos />
    </HydrationBoundary>
  )
}

queryClient.prefetchQuery mengisi cache di server, lalu dehydrate(queryClient) mengubah cache menjadi state serializable yang dikirim bersama HTML. HydrationBoundary state={dehydrate(queryClient)} memulihkan cache di client — komponen Todos yang memakai useQuery langsung menemukan data dan tidak melakukan fetch ulang.

Remix dan Framework Lain

Remix Loaders

Di Remix, data diambil di loader server dan diteruskan ke komponen. Untuk memadukannya dengan TanStack Query, seed cache dengan data loader lewat initialData di client:

JSSeed cache dari loader Remix
export async function loader() {
  const todos = await fetchTodos()
  return { todos }
}
 
export default function Todos({ loaderData }) {
  const { data } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
    initialData: loaderData.todos,
    staleTime: 60 * 1000,
  })
  return <TodoList todos={data} />
}

initialData: loaderData.todos mengisi cache dari data yang sudah diambil loader. useQuery menampilkan data itu seketika dan menyegarkannya di latar belakang setelah staleTime habis. Pendekatan ini adalah cara paling sederhana memadukan Remix dan TanStack Query tanpa hydration boundary.

Pola Framework-Agnostic

Karena logika inti hidup di @tanstack/query-core, semua adapter framework memakai konsep yang sama — query key, cache, observer. Ilmu yang kalian pelajari di series ini berlaku untuk Vue Query, Svelte Query, dan Solid Query tanpa perubahan konsep, hanya beda sintaks hook. Episode 19 akan membahas ekosistem adapter ini secara menyeluruh.

Warning

Di Next.js, jangan pernah membuat QueryClient baru di dalam setiap render komponen. Gunakan pola getQueryClient agar server membuat satu instance per request dan browser memakai satu instance untuk seluruh sesi — kalau tidak, cache akan reset terus menerus.

Penutup

Episode 14 menjembatani TanStack Query dengan SSR: prefetch di server, dehydrate, hydrate lewat HydrationBoundary, pola getQueryClient untuk Next.js App Router, dan seeding cache dari loader Remix. Kalian juga melihat bahwa konsep intinya framework-agnostic.

Inti yang harus dibawa pulang:

  • dehydrate membekukan cache; hydrate memulihkannya di client.
  • HydrationBoundary menghubungkan cache server dan client.
  • getQueryClient membedakan instance server dan browser.
  • Server components bisa prefetch sebelum render.
  • Remix bisa seed cache dengan initialData dari loader.
  • Konsep inti library berlaku di semua framework adapter.

Di episode 15 selanjutnya kita akan membahas security dan best practice — menangani 401/403 dengan refresh token, menghindari data sensitif berlebihan di cache, sanitasi error, serta best practice query key dan pemisahan server state versus client state.

Belajar TanStack Query - SSR & Framework Integration | Belajar TanStack Query