> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/tanstack/query/llms.txt
> Use this file to discover all available pages before exploring further.

# Quick Start

> Get up and running with Svelte Query in minutes

This guide will walk you through creating your first Svelte Query application, covering queries, mutations, and common patterns.

## Prerequisites

Before starting, make sure you have:

* Svelte 5.25.0 or higher installed
* A Svelte or SvelteKit project set up
* `@tanstack/svelte-query` installed

If you haven't installed Svelte Query yet, see the [Installation Guide](/frameworks/svelte/installation).

## Your First Query

<Steps>
  <Step title="Set up the QueryClient">
    First, create a `QueryClient` and wrap your app with `QueryClientProvider` in your root layout:

    ```svelte +layout.svelte theme={null}
    <script lang="ts">
    import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query'

    const queryClient = new QueryClient()
    const { children } = $props()
    </script>

    <QueryClientProvider client={queryClient}>
      {@render children()}
    </QueryClientProvider>
    ```
  </Step>

  <Step title="Create your first query">
    Now create a component that fetches data using `createQuery`:

    ```svelte Posts.svelte theme={null}
    <script lang="ts">
    import { createQuery } from '@tanstack/svelte-query'

    interface Post {
      id: number
      title: string
      body: string
    }

    async function fetchPosts(): Promise<Post[]> {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts')
      if (!response.ok) throw new Error('Failed to fetch posts')
      return response.json()
    }

    const postsQuery = createQuery(() => ({
      queryKey: ['posts'],
      queryFn: fetchPosts,
    }))
    </script>

    <div>
      <h1>Posts</h1>
      
      {#if postsQuery.isPending}
        <p>Loading posts...</p>
      {:else if postsQuery.isError}
        <p>Error: {postsQuery.error.message}</p>
      {:else}
        <ul>
          {#each postsQuery.data as post}
            <li>
              <h2>{post.title}</h2>
              <p>{post.body}</p>
            </li>
          {/each}
        </ul>
      {/if}
    </div>
    ```

    <Note>
      The `createQuery` function takes an accessor function `() => options` that returns the query configuration. This allows the query to react to changes in dependencies.
    </Note>
  </Step>

  <Step title="Understanding query states">
    Svelte Query provides several state properties to handle different scenarios:

    ```svelte theme={null}
    {#if postsQuery.isPending}
      <!-- Query is loading for the first time -->
      <Spinner />
    {:else if postsQuery.isError}
      <!-- Query encountered an error -->
      <ErrorMessage error={postsQuery.error} />
    {:else if postsQuery.isSuccess}
      <!-- Query succeeded and data is available -->
      <DataView data={postsQuery.data} />
    {/if}

    <!-- Background refetch indicator -->
    {#if postsQuery.isFetching}
      <div class="refetch-indicator">Updating...</div>
    {/if}
    ```

    **Key states:**

    * `isPending` - Query has no data yet (initial load)
    * `isError` - Query failed
    * `isSuccess` - Query succeeded
    * `isFetching` - Query is fetching (includes background refetches)
    * `data` - The actual query data
    * `error` - The error object if query failed
  </Step>
</Steps>

## Dynamic Queries

Queries can depend on reactive variables. The query automatically refetches when dependencies change:

```svelte PostDetail.svelte theme={null}
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'

let postId = $state(1)

const postQuery = createQuery(() => ({
  queryKey: ['post', postId],
  queryFn: async () => {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/posts/${postId}`
    )
    return response.json()
  },
}))
</script>

<div>
  <button onclick={() => postId--}>Previous</button>
  <button onclick={() => postId++}>Next</button>

  {#if postQuery.data}
    <h1>{postQuery.data.title}</h1>
    <p>{postQuery.data.body}</p>
  {/if}
</div>
```

<Tip>
  When `postId` changes, the query key `['post', postId]` changes, triggering an automatic refetch with the new ID.
</Tip>

## Mutations

Use `createMutation` to create, update, or delete data:

```svelte CreatePost.svelte theme={null}
<script lang="ts">
import { createMutation, useQueryClient } from '@tanstack/svelte-query'

const queryClient = useQueryClient()

interface NewPost {
  title: string
  body: string
}

const createPostMutation = createMutation(() => ({
  mutationFn: async (newPost: NewPost) => {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newPost),
    })
    return response.json()
  },
  onSuccess: () => {
    // Invalidate and refetch posts query
    queryClient.invalidateQueries({ queryKey: ['posts'] })
  },
}))

let title = $state('')
let body = $state('')

function handleSubmit() {
  createPostMutation.mutate({ title, body })
  title = ''
  body = ''
}
</script>

<form onsubmit={handleSubmit}>
  <input bind:value={title} placeholder="Title" />
  <textarea bind:value={body} placeholder="Body" />
  
  <button 
    type="submit" 
    disabled={createPostMutation.isPending}
  >
    {createPostMutation.isPending ? 'Creating...' : 'Create Post'}
  </button>

  {#if createPostMutation.isError}
    <p class="error">{createPostMutation.error.message}</p>
  {/if}
</form>
```

### Mutation States

Mutations provide similar state properties:

* `isPending` - Mutation is in progress
* `isError` - Mutation failed
* `isSuccess` - Mutation succeeded
* `data` - The mutation result data
* `error` - The error object if mutation failed
* `mutate()` - Function to trigger the mutation
* `mutateAsync()` - Promise-based mutation function

## Query Options

Customize query behavior with various options:

```svelte theme={null}
const query = createQuery(() => ({
  queryKey: ['posts', { status: 'published' }],
  queryFn: fetchPublishedPosts,
  
  // Refetch interval (ms)
  refetchInterval: 10000,
  
  // Only refetch on window focus if data is stale
  refetchOnWindowFocus: 'always',
  
  // How long data stays fresh
  staleTime: 5 * 60 * 1000, // 5 minutes
  
  // Cache time
  gcTime: 10 * 60 * 1000, // 10 minutes
  
  // Retry failed requests
  retry: 3,
  
  // Enable/disable the query
  enabled: true,
}))
```

<Note>
  All time values are in milliseconds. Use `staleTime` to control when data is considered "stale" and needs refetching.
</Note>

## Infinite Queries

For paginated or infinite scroll data, use `createInfiniteQuery`:

```svelte InfinitePosts.svelte theme={null}
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'

interface PostsResponse {
  posts: Array<{ id: number; title: string }>
  nextCursor: number | null
}

const query = createInfiniteQuery(() => ({
  queryKey: ['posts', 'infinite'],
  queryFn: async ({ pageParam = 1 }) => {
    const response = await fetch(`/api/posts?page=${pageParam}`)
    return response.json() as Promise<PostsResponse>
  },
  initialPageParam: 1,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
</script>

<div>
  {#if query.data}
    {#each query.data.pages as page}
      {#each page.posts as post}
        <article>
          <h2>{post.title}</h2>
        </article>
      {/each}
    {/each}
  {/if}

  <button
    onclick={() => query.fetchNextPage()}
    disabled={!query.hasNextPage || query.isFetchingNextPage}
  >
    {#if query.isFetchingNextPage}
      Loading more...
    {:else if query.hasNextPage}
      Load More
    {:else}
      No more posts
    {/if}
  </button>
</div>
```

### Infinite Query Properties

* `data.pages` - Array of all fetched pages
* `data.pageParams` - Array of all page parameters
* `hasNextPage` - Whether more pages are available
* `hasPreviousPage` - Whether previous pages are available
* `fetchNextPage()` - Load the next page
* `fetchPreviousPage()` - Load the previous page
* `isFetchingNextPage` - Next page is loading
* `isFetchingPreviousPage` - Previous page is loading

## Query Invalidation

Invalidate queries to force them to refetch:

```svelte theme={null}
<script lang="ts">
import { useQueryClient } from '@tanstack/svelte-query'

const queryClient = useQueryClient()

// Invalidate all queries
queryClient.invalidateQueries()

// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['posts'] })

// Invalidate queries matching a pattern
queryClient.invalidateQueries({ queryKey: ['posts', { status: 'draft' }] })
</script>
```

## Using queryOptions Helper

For better type safety and reusability, use the `queryOptions` helper:

```typescript queries.ts theme={null}
import { queryOptions } from '@tanstack/svelte-query'

export const postsQueryOptions = queryOptions({
  queryKey: ['posts'],
  queryFn: async () => {
    const response = await fetch('/api/posts')
    return response.json()
  },
  staleTime: 5 * 60 * 1000,
})
```

Then use it in your components:

```svelte Posts.svelte theme={null}
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'
import { postsQueryOptions } from './queries'

const postsQuery = createQuery(() => postsQueryOptions)
</script>
```

<Tip>
  The `queryOptions` helper provides better type inference and makes it easier to share query configurations across components.
</Tip>

## Best Practices

<Steps>
  <Step title="Use meaningful query keys">
    Query keys should describe the data uniquely:

    ```typescript theme={null}
    // Good
    ['posts', { status: 'published', author: userId }]
    ['user', userId]
    ['todos', { filter: 'completed' }]

    // Bad
    ['data']
    ['fetch']
    ['query1']
    ```
  </Step>

  <Step title="Handle loading and error states">
    Always provide feedback for pending and error states:

    ```svelte theme={null}
    {#if query.isPending}
      <LoadingSpinner />
    {:else if query.isError}
      <ErrorMessage error={query.error} />
    {:else}
      <DataDisplay data={query.data} />
    {/if}
    ```
  </Step>

  <Step title="Configure staleTime appropriately">
    Set `staleTime` based on how often your data changes:

    ```typescript theme={null}
    // User profile (rarely changes)
    staleTime: 10 * 60 * 1000 // 10 minutes

    // Real-time data (changes frequently)
    staleTime: 0

    // Dashboard stats (moderate updates)
    staleTime: 60 * 1000 // 1 minute
    ```
  </Step>

  <Step title="Invalidate queries after mutations">
    Keep your UI in sync by invalidating related queries:

    ```typescript theme={null}
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['posts'] })
      queryClient.invalidateQueries({ queryKey: ['user', userId] })
    }
    ```
  </Step>
</Steps>

## Common Patterns

### Dependent Queries

Execute a query only after another query succeeds:

```svelte theme={null}
<script lang="ts">
let userId = $state(1)

const userQuery = createQuery(() => ({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
}))

const projectsQuery = createQuery(() => ({
  queryKey: ['projects', userQuery.data?.id],
  queryFn: () => fetchUserProjects(userQuery.data!.id),
  enabled: !!userQuery.data,
}))
</script>
```

### Optimistic Updates

Update UI immediately before server confirmation:

```typescript theme={null}
const mutation = createMutation(() => ({
  mutationFn: updatePost,
  onMutate: async (newPost) => {
    await queryClient.cancelQueries({ queryKey: ['posts'] })
    const previousPosts = queryClient.getQueryData(['posts'])
    queryClient.setQueryData(['posts'], (old) => [...old, newPost])
    return { previousPosts }
  },
  onError: (err, newPost, context) => {
    queryClient.setQueryData(['posts'], context.previousPosts)
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['posts'] })
  },
}))
```

### Prefetching

Prefetch data before it's needed:

```svelte theme={null}
<script lang="ts">
import { useQueryClient } from '@tanstack/svelte-query'

const queryClient = useQueryClient()

function handleMouseEnter(postId: number) {
  queryClient.prefetchQuery({
    queryKey: ['post', postId],
    queryFn: () => fetchPost(postId),
  })
}
</script>

<a href="/post/{post.id}" onmouseenter={() => handleMouseEnter(post.id)}>
  {post.title}
</a>
```

## Next Steps

<Steps>
  <Step title="TypeScript Integration">
    Learn how to get full type safety with TypeScript.

    [TypeScript Guide →](/frameworks/svelte/typescript)
  </Step>

  <Step title="DevTools">
    Install and use the Svelte Query DevTools for debugging.

    [DevTools Setup →](/frameworks/svelte/devtools)
  </Step>

  <Step title="Advanced Guides">
    Explore advanced patterns like SSR, persisting, and more.

    [Guides →](/guides/important-defaults)
  </Step>
</Steps>
