惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
Stack Overflow Blog
Stack Overflow Blog
B
Blog RSS Feed
C
Check Point Blog
D
Docker
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
MongoDB | Blog
MongoDB | Blog
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
量子位
有赞技术团队
有赞技术团队
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
D
DataBreaches.Net
M
MIT News - Artificial intelligence
B
Blog
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
月光博客
月光博客

Supabase Blog

AI Agents Know About Supabase. They Don't Always Use It Right. Custom OIDC Providers for Supabase Auth 100,000 GitHub stars Supabase docs over SSH Navigating Regional Network Blocks Supabase Joins the Stripe Projects Developer Preview Log Drains: Now available on Pro Supabase Storage: major performance, security, and reliability updates Supabase incident on February 12, 2026 Hydra joins Supabase X / Twitter OAuth 2.0 is now available for Supabase Auth BKND joins Supabase Supabase is now an official Claude connector Supabase PrivateLink is now available Introducing: Postgres Best Practices When to use Read Replicas vs. bigger compute Introducing TRAE SOLO integration with Supabase Supabase Security Retro: 2025 Sync Stripe Data to Your Supabase Database in One Click Building ChatGPT Apps with Supabase Edge Functions and mcp-use Own Your Observability: Supabase Metrics API Introducing iceberg-js: A JavaScript Client for Apache Iceberg Introducing Supabase for Platforms Adding Async Streaming to Postgres Foreign Data Wrappers Build "Sign in with Your App" using Supabase Auth Introducing Seven New Email Templates for Supabase Auth The new Supabase power for Kiro Introducing Supabase ETL Introducing Analytics Buckets Introducing Vector Buckets
Using React Query with Next.js App Router and Supabase Ca...
Giancarlo Buomprisco, Philipp Steinrötter, Thor Schaeff · 2024-01-12 · via Supabase Blog

Using React Query with Next.js App Router and Supabase Cache Helpers

TanStack Query, also known as React Query, is an open source state management library for React which handles caching, background updates and stale data out of the box with zero-configuration, which makes it an ideal tool to pair with supabase-js and our auto-generated REST API!

If you prefer video guides, we've got a three-part video series for you!

If you learn better by just jumping into a demo application, you can find one in our examples on GitHub.

Note: this blogpost is inspired by Giancarlo's original blogpost on using React Query with Supabase in Remix.io!

This article assumes that your have some basic kowledge of building React applications with Next.js. No prior knowledge of React Query or Supabase is required.

We will use the following tools

Install the required dependencies#

After you have created your Next.js project, e.g. with npx create-next-app@latest, you can install the required dependencies using the following command:


_10

npm install @supabase/supabase-js @tanstack/react-query @supabase/ssr @supabase-cache-helpers/postgrest-react-query


Creating a React Query client#

Create a React Query client in the root of your component tree. In Next.js app router applications, this is the layout.tsx file in the app folder.

The QueryClientProvider can only be used in client components and can't be directly embedded in the layout.tsx file. Therefore make sure to create a client component first, e.g.

components/ReactQueryClientProvider.tsx


_20

'use client'

_20

_20

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

_20

import { useState } from 'react'

_20

_20

export const ReactQueryClientProvider = ({ children }: { children: React.ReactNode }) => {

_20

const [queryClient] = useState(

_20

() =>

_20

new QueryClient({

_20

defaultOptions: {

_20

queries: {

_20

// With SSR, we usually want to set some default staleTime

_20

// above 0 to avoid refetching immediately on the client

_20

staleTime: 60 * 1000,

_20

},

_20

},

_20

})

_20

)

_20

return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>

_20

}


Next, wrap the root in layout.tsx:


_21

import type { Metadata } from 'next'

_21

import { Inter } from 'next/font/google'

_21

import './globals.css'

_21

import { ReactQueryClientProvider } from '@/components/ReactQueryClientProvider'

_21

_21

const inter = Inter({ subsets: ['latin'] })

_21

_21

export const metadata: Metadata = {

_21

title: 'Create Next App',

_21

description: 'Generated by create next app',

_21

}

_21

_21

export default function RootLayout({ children }: { children: React.ReactNode }) {

_21

return (

_21

<ReactQueryClientProvider>

_21

<html lang="en">

_21

<body className={inter.className}>{children}</body>

_21

</html>

_21

</ReactQueryClientProvider>

_21

)

_21

}


Creating your Database schema and generating TypeScript types#

For this example, we'll use a simple countries table where we store the id and name of countries. In your Supabase Dashboard SQL editor create the countries table and add some values:


_11

create table countries (

_11

"id" serial primary key,

_11

"name" text

_11

);

_11

_11

insert into countries

_11

(id, name)

_11

values

_11

(1, 'United Kingdom'),

_11

(2, 'United States'),

_11

(3, 'Singapore');


Once you've created your schema, you can use the Supabase CLI to automatically generate TypeScript types for you:


_10

supabase login

_10

supabase init

_10

supabase link

_10

supabase gen types typescript --linked --schema=public > utils/database.types.ts


These generated types will allow us to get typed data returned from React Query.

Creating supabase-js clients for client and server components#

To help you utilize the full power of supabase-js, including Supabase Auth and Row Level Security (RLS) policies, we provide the Supabase SSR helper library that allows you to conveniently create both browser Supabase clients for client components and server Supabase clients for server components.

Further reading: detailed documentation for Supabase SSR in Next.js

Create a TypedSupabaseClient type#

To make sure we have the proper typing available in all our components, we can create a TypedSupabaseClient type that we can hand to React Query:


_10

import { SupabaseClient } from '@supabase/supabase-js'

_10

import type { Database } from '@/utils/database.types'

_10

_10

export type TypedSupabaseClient = SupabaseClient<Database>


Creating a Browser Supabase Client#

utils/supabase-browser.ts


_25

import { createBrowserClient } from '@supabase/ssr'

_25

import type { Database } from '@/utils/database.types'

_25

import type { TypedSupabaseClient } from '@/utils/types'

_25

import { useMemo } from 'react'

_25

_25

let client: TypedSupabaseClient | undefined

_25

_25

function getSupabaseBrowserClient() {

_25

if (client) {

_25

return client

_25

}

_25

_25

client = createBrowserClient<Database>(

_25

process.env.NEXT_PUBLIC_SUPABASE_URL!,

_25

process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

_25

)

_25

_25

return client

_25

}

_25

_25

function useSupabaseBrowser() {

_25

return useMemo(getSupabaseBrowserClient, [])

_25

}

_25

_25

export default useSupabaseBrowser


Creating a Server Supabase Client#


_17

import { createServerClient } from '@supabase/ssr'

_17

import { cookies } from 'next/headers'

_17

import { Database } from './database.types'

_17

_17

export default function useSupabaseServer(cookieStore: ReturnType<typeof cookies>) {

_17

return createServerClient<Database>(

_17

process.env.NEXT_PUBLIC_SUPABASE_URL!,

_17

process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,

_17

{

_17

cookies: {

_17

get(name: string) {

_17

return cookieStore.get(name)?.value

_17

},

_17

},

_17

}

_17

)

_17

}


Now we've got everything in place to get started fetching and caching data with React Query!

Automate query key management with the Supabase Cache Helpers#

React Query manages query caching based on query keys. Needing to manage query keys is somewhat burdensome, luckily this is where the Supabase Cache Helpers come into play.

Initially built during the Launch Week 5 Hackathon by Philipp Steinrötter, it has become a full blown open source project that automatically generates cache keys from your supabase-js queries, amongst many other awesome features!

Write reusable queries#

The most convenient way to use your queries across both server and client component is to define them in a central place, e.g. a queries folder:

queries/get-country-by-id.ts


_15

import { TypedSupabaseClient } from '@/utils/types'

_15

_15

export function getCountryById(client: TypedSupabaseClient, countryId: number) {

_15

return client

_15

.from('countries')

_15

.select(

_15

`

_15

id,

_15

name

_15

`

_15

)

_15

.eq('id', countryId)

_15

.throwOnError()

_15

.single()

_15

}


This is a simple query function that takes in either the browser or the server Supabase client and the id of a country, and returns a supabase-js query.

Fetch data server side#

In server components, we can now use this query with the prefetchQuery method:

app/ssrcountries/[id]/page.tsx


_22

import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'

_22

import { prefetchQuery } from '@supabase-cache-helpers/postgrest-react-query'

_22

import useSupabaseServer from '@/utils/supabase-server'

_22

import { cookies } from 'next/headers'

_22

import Country from '../country'

_22

import { getCountryById } from '@/queries/get-country-by-id'

_22

_22

export default async function CountryPage({ params }: { params: { id: number } }) {

_22

const queryClient = new QueryClient()

_22

const cookieStore = cookies()

_22

const supabase = useSupabaseServer(cookieStore)

_22

_22

await prefetchQuery(queryClient, getCountryById(supabase, params.id))

_22

_22

return (

_22

// Neat! Serialization is now as easy as passing props.

_22

// HydrationBoundary is a Client Component, so hydration will happen there.

_22

<HydrationBoundary state={dehydrate(queryClient)}>

_22

<Country id={params.id} />

_22

</HydrationBoundary>

_22

)

_22

}


Our query will be executed and fetch the data on the server. This means when using our query in the corresponding Country client component, the data will be immediately available upon render:

app/ssrcountries/country.tsx


_18

'use client'

_18

_18

import useSupabaseBrowser from '@/utils/supabase-browser'

_18

import { getCountryById } from '@/queries/get-country-by-id'

_18

import { useQuery } from '@supabase-cache-helpers/postgrest-react-query'

_18

_18

export default function Country({ id }: { id: number }) {

_18

const supabase = useSupabaseBrowser()

_18

// This useQuery could just as well happen in some deeper

_18

// child to <Posts>, data will be available immediately either way

_18

const { data: country } = useQuery(getCountryById(supabase, id))

_18

_18

return (

_18

<div>

_18

<h1>SSR: {country?.name}</h1>

_18

</div>

_18

)

_18

}


Since our query has them same generated cache key, React Query knows that the data was pre-fetched server side and therefore can render immediately without any loading state.

Fetch data client side#

Of course you can still combine this with fetching data client side. React Query will check if a given query was pre-fetched server side, but if it wasn't it will then go ahead and fetch the data client side side using the browser Supabase client:

app/countries/[id]/page.tsx


_24

'use client'

_24

_24

import useSupabaseBrowser from '@/utils/supabase-browser'

_24

import { getCountryById } from '@/queries/get-country-by-id'

_24

import { useQuery } from '@supabase-cache-helpers/postgrest-react-query'

_24

_24

export default function CountryPage({ params }: { params: { id: number } }) {

_24

const supabase = useSupabaseBrowser()

_24

const { data: country, isLoading, isError } = useQuery(getCountryById(supabase, params.id))

_24

_24

if (isLoading) {

_24

return <div>Loading...</div>

_24

}

_24

_24

if (isError || !country) {

_24

return <div>Error</div>

_24

}

_24

_24

return (

_24

<div>

_24

<h1>{country.name}</h1>

_24

</div>

_24

)

_24

}


React Query and the Supabase Cache Helpers are fantastic tools to help you manage data fetching and caching in your Next.js applications.

Using React Query with Server Components makes most sense if:

  • You have an app using React Query and want to migrate to Server Components without rewriting all the data fetching.
  • You want a familiar programming paradigm, but want to still sprinkle in the benefits of Server Components where it makes most sense.
  • You have some use case that React Query covers, but that your framework of choice does not cover.

It's hard to give general advice on when it makes sense to pair React Query with Server Components and not. If you are just starting out with a new Server Components app, we suggest you start out with any tools for data fetching your framework provides you with and avoid bringing in React Query until you actually need it. This might be never, and that's fine, as always: use the right tool for the job!