Guides
Server-Side Rendering
SSR and hydration setup for Eden TanStack Query
Server-Side Rendering
Eden TanStack Query supports SSR with createEdenOptionsProxy, which creates a server-side proxy for prefetching data that hydrates seamlessly on the client.
createEdenOptionsProxy
The key to SSR is createEdenOptionsProxy -- it creates an Eden proxy bound to a specific client and QueryClient, generating the same query keys as the client-side useEden() hook:
import { createEdenOptionsProxy } from 'eden-tanstack-react-query'
const eden = createEdenOptionsProxy<App>({
client: serverClient,
queryClient
})
// Use the same API as useEden()
await queryClient.prefetchQuery(eden.users.get.queryOptions())Next.js App Router
Layout and Providers
import { Providers } from './providers'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
)
}'use client'
import { useState } from 'react'
import { QueryClientProvider } from '@tanstack/react-query'
import { getQueryClient } from '@/lib/query-client'
import { EdenProvider, edenClient } from '@/lib/eden'
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => getQueryClient())
return (
<QueryClientProvider client={queryClient}>
<EdenProvider client={edenClient} queryClient={queryClient}>
{children}
</EdenProvider>
</QueryClientProvider>
)
}QueryClient Factory
import { QueryClient } from '@tanstack/react-query'
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
}
}
})
}
let browserQueryClient: QueryClient | undefined
export function getQueryClient() {
if (typeof window === 'undefined') {
return makeQueryClient()
}
if (!browserQueryClient) {
browserQueryClient = makeQueryClient()
}
return browserQueryClient
}Server Component Prefetching
import { HydrationBoundary, dehydrate } from '@tanstack/react-query'
import { createEdenOptionsProxy } from 'eden-tanstack-react-query'
import { getQueryClient } from '@/lib/query-client'
import { getServerEdenClient } from '@/lib/eden'
import type { App } from '@/lib/server'
import { UserList } from './user-list'
export default async function UsersPage() {
const queryClient = getQueryClient()
const serverClient = getServerEdenClient()
const eden = createEdenOptionsProxy<App>({
client: serverClient,
queryClient
})
// Prefetch on server
await queryClient.prefetchQuery(eden.users.get.queryOptions())
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<UserList />
</HydrationBoundary>
)
}'use client'
import { useQuery } from '@tanstack/react-query'
import { useEden } from '@/lib/eden'
export function UserList() {
const eden = useEden()
// Uses prefetched data -- no loading state on initial render
const { data: users } = useQuery(eden.users.get.queryOptions())
return (
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}Prefetching Multiple Queries
export default async function DashboardPage() {
const queryClient = getQueryClient()
const eden = createEdenOptionsProxy<App>({ client: getServerEdenClient(), queryClient })
await Promise.all([
queryClient.prefetchQuery(eden.users.get.queryOptions()),
queryClient.prefetchQuery(eden.stats.get.queryOptions()),
])
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Dashboard />
</HydrationBoundary>
)
}Next.js Pages Router
The same pattern works with getServerSideProps:
import type { GetServerSideProps } from 'next'
import { dehydrate, QueryClient } from '@tanstack/react-query'
import { createEdenOptionsProxy } from 'eden-tanstack-react-query'
import { treaty } from '@elysiajs/eden'
import type { App } from '@/lib/server'
export const getServerSideProps: GetServerSideProps = async () => {
const queryClient = new QueryClient()
const serverClient = treaty<App>(process.env.INTERNAL_API_URL!)
const eden = createEdenOptionsProxy<App>({ client: serverClient, queryClient })
await queryClient.prefetchQuery(eden.users.get.queryOptions())
return {
props: { dehydratedState: dehydrate(queryClient) }
}
}Wrap your app with HydrationBoundary in _app.tsx and provide EdenProvider as shown in the Getting Started guide. See the TanStack Query SSR docs for the full Pages Router setup.
TanStack Start
Use ensureQueryData in route loaders for server-side prefetching:
import { createFileRoute } from '@tanstack/react-router'
import { createEdenOptionsProxy } from 'eden-tanstack-react-query'
import { treaty } from '@elysiajs/eden'
import type { App } from '@/lib/server'
export const Route = createFileRoute('/users')({
loader: async ({ context: { queryClient } }) => {
const serverClient = treaty<App>(import.meta.env.VITE_API_URL)
const eden = createEdenOptionsProxy<App>({ client: serverClient, queryClient })
await queryClient.ensureQueryData(eden.users.get.queryOptions())
},
component: UsersPage
})See the TanStack Start docs for router and QueryClient setup.