Quick Start

Purpose and Scope

This quick start gives you the shortest path to a working TanStack Query setup in a React application: create one QueryClient, place a QueryClientProvider near the root of the app, and call useQuery from a component with a stable queryKey and an asynchronous queryFn. TanStack Query is designed for asynchronous server state, not local component state, so the first setup should prove three things: the app has a client-owned cache, components can subscribe to cached data, and query functions can fetch from any backend or promise-returning API.

Sources: docs/framework/preact/quick-start.md, docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

The supplied framework quick-starts show the same mental model through different framework APIs. The Preact quick-start is explicitly generated from the React quick-start with terminology and package-name replacements, which signals that Preact follows the React-style provider and hook shape. Lit expresses the same idea as a QueryClientProvider custom element plus reactive controllers, while Angular uses dependency injection with provideTanStackQuery(new QueryClient()) and injectQuery. These adapters differ in syntax, but they all begin by making a QueryClient available to application code before any query is created.

Sources: docs/framework/preact/quick-start.md, docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

Core Primitives

The first primitive is QueryClient. It owns the query cache and is the object that later receives operations such as invalidateQueries. In the Lit quick-start, const queryClient = new QueryClient() is created once and assigned to a provider class. In the Angular quick-start, new QueryClient() is passed to provideTanStackQuery, and a component later injects QueryClient so a successful mutation can invalidate the ['todos'] query. In React, create the client outside rendering work or inside a stable initializer so a re-render does not accidentally replace the cache.

Sources: docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

The second primitive is the provider. A provider connects framework code to the shared QueryClient. Lit demonstrates this by subclassing QueryClientProvider, setting this.client = queryClient, and mounting the provider around <todos-view>. Angular demonstrates the same boundary at application bootstrap by installing provideTanStackQuery(new QueryClient()) in the app providers. In React, QueryClientProvider plays this role directly: wrap the component tree that calls TanStack Query hooks and pass the client through the client prop.

Sources: docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

The third primitive is the query consumer. In React the consumer is useQuery; in Preact it follows the React-derived quick-start contract; in Lit it is createQueryController(this, { queryKey, queryFn }); and in Angular it is injectQuery(() => ({ queryKey, queryFn })). The common contract is more important than the adapter syntax: every query needs a key that identifies the cached resource and a function that returns the data, usually by resolving a promise from fetch, a GraphQL client, Angular HttpClient, or another service layer.

Sources: docs/framework/preact/quick-start.md, docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

First Working React Setup

Start by installing the React adapter in your application, then create the client and provider at the root. The exact root file depends on your app scaffold, but the important rule is that the provider must wrap any component that calls useQuery. The Lit and Angular quick-starts both put the client at the application boundary before defining the todos view, which is the same sequencing React applications should follow. Keeping the client at the root also makes later cache operations, devtools, hydration, and invalidation patterns easier to reason about.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createRoot } from 'react-dom/client'
import App from './App'
 
const queryClient = new QueryClient()
 
createRoot(document.getElementById('root')!).render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>,
)

Next, call useQuery from a component. The example below uses a ['todos'] key because the Angular and Lit quick-starts both model a todo list and then invalidate that same key after creating a todo. The query function is deliberately backend-agnostic: it can be a direct fetch, a generated client call, or a service function. What matters for TanStack Query is that the function returns a promise for the data represented by the key.

import { useQuery } from '@tanstack/react-query'
 
type Todo = {
  id: string
  title: string
}
 
async function getTodos(): Promise<Todo[]> {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos')
  return response.json()
}
 
export function Todos() {
  const query = useQuery({
    queryKey: ['todos'],
    queryFn: getTodos,
  })
 
  if (query.isPending) return <p>Loading...</p>
  if (query.isError) return <p>Error: {query.error.message}</p>
 
  return (
    <ul>
      {query.data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

Execution Flow

When the React app renders, QueryClientProvider puts the client into React context. When Todos renders, useQuery registers an observer for the ['todos'] key and starts the getTodos promise if the data is not already available or fresh. While the promise is unresolved, the result exposes pending state for loading UI. When data arrives, the cache stores it under the key and the subscribed component receives the updated result. This mirrors the Lit quick-start, where the controller is called during render and exposes isPending, isError, error, and data.

Sources: docs/framework/lit/quick-start.md

After the first query works, mutations introduce the cache-synchronization step. Both the Lit and Angular quick-starts create a todo mutation and call queryClient.invalidateQueries({ queryKey: ['todos'] }) on success. In React, the corresponding pattern is useMutation plus the same invalidation call from a client obtained with useQueryClient. Invalidation marks related query data stale and gives TanStack Query a precise reason to refetch, so the UI can stay synchronized after writes without manually threading server responses through every component.

Sources: docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

System-to-Code Mapping

ConceptReact quick-start shapeEvidence from supplied framework docs
Client-owned cacheconst queryClient = new QueryClient()Lit creates a QueryClient; Angular passes a QueryClient to provideTanStackQuery.
App boundary<QueryClientProvider client={queryClient}>Lit mounts a provider around the view; Angular installs a provider during bootstrap or NgModule setup.
Query readuseQuery({ queryKey, queryFn })Lit uses createQueryController with queryKey and queryFn; Angular uses injectQuery with the same option names.
Write synchronizationinvalidateQueries({ queryKey: ['todos'] }) after successLit and Angular quick-starts both invalidate the ['todos'] query after a todo mutation succeeds.
Adapter-specific APIReact hooksPreact quick-start references the React quick-start with React-to-Preact and package-name replacement metadata.

The mapping is useful because it keeps adapter details from hiding the shared server-state workflow. React developers should not treat the provider as a theme-style wrapper or the key as a display label. The provider is the connection to the cache, and the key is the cache identity. If two components use the same key and compatible query functions, they subscribe to the same cached resource. If a mutation changes that resource, invalidating the same key tells the client which observers may need fresh data.

Sources: docs/framework/preact/quick-start.md, docs/framework/lit/quick-start.md, docs/framework/angular/quick-start.md

Relevant Source Files

  • docs/framework/preact/quick-start.md - Defines the Preact quick-start as a reference to the React quick-start with package and framework terminology replacements, supporting the React-style provider and hook orientation for this page.
  • docs/framework/lit/quick-start.md - Shows the shared QueryClient, QueryClientProvider, query creation, mutation creation, status handling, and invalidation concepts in a Lit adapter form.
  • docs/framework/angular/quick-start.md - Shows application-level client provisioning, injectQuery, injectMutation, service-backed promise fetching, and invalidateQueries after mutation success.

Next Steps

Once this page is working, continue by deepening one concept at a time instead of adding every feature at once. Read Queries to understand status fields and query lifecycles, Query Keys to design cache identity, Mutations to perform writes, and Invalidations from Mutations to keep lists and detail views synchronized. If you are using a non-React framework, compare the adapter page for your framework with this setup: the names may change, but the client, provider, query options, and invalidation workflow remain the core building blocks.