> ## 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.

# Solid Query Quick Start

> Build your first Solid Query application in minutes

# Quick Start Guide

Get up and running with Solid Query in just a few minutes. This guide will walk you through creating a simple application that fetches and displays data.

## Complete Example

Here's a complete working example to get you started:

```tsx theme={null}
import { render } from 'solid-js/web'
import { For, Show, Suspense } from 'solid-js'
import { 
  QueryClient, 
  QueryClientProvider, 
  useQuery,
  useMutation,
  useQueryClient,
} from '@tanstack/solid-query'

// Create a client
const queryClient = new QueryClient()

// Define your data types
interface Post {
  id: number
  title: string
  body: string
}

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

// Component that uses the query
function Posts() {
  const postsQuery = useQuery(() => ({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
  }))

  return (
    <div>
      <h2>Posts</h2>
      <Show when={postsQuery.isLoading}>
        <div>Loading...</div>
      </Show>
      <Show when={postsQuery.isError}>
        <div>Error: {postsQuery.error?.message}</div>
      </Show>
      <Show when={postsQuery.data}>
        {(posts) => (
          <ul>
            <For each={posts().slice(0, 10)}>
              {(post) => (
                <li>
                  <strong>{post.title}</strong>
                  <p>{post.body}</p>
                </li>
              )}
            </For>
          </ul>
        )}
      </Show>
      <button onClick={() => postsQuery.refetch()}>
        Refetch
      </button>
    </div>
  )
}

// App component with provider
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div style={{ padding: '20px' }}>
        <h1>Solid Query Quick Start</h1>
        <Posts />
      </div>
    </QueryClientProvider>
  )
}

render(() => <App />, document.getElementById('root')!)
```

## Step-by-Step Guide

Let's break down the key concepts:

<Steps>
  ### Install Solid Query

  ```bash theme={null}
  npm install @tanstack/solid-query
  ```

  ### Set Up the Query Client

  Create a `QueryClient` instance and wrap your app with `QueryClientProvider`:

  ```tsx theme={null}
  import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'

  const queryClient = new QueryClient()

  function App() {
    return (
      <QueryClientProvider client={queryClient}>
        {/* Your app components */}
      </QueryClientProvider>
    )
  }
  ```

  <Note>
    The `QueryClientProvider` makes the query client available to all components in your app.
  </Note>

  ### Create Your First Query

  Use `useQuery` to fetch data:

  ```tsx theme={null}
  import { useQuery } from '@tanstack/solid-query'

  function TodoList() {
    const todosQuery = useQuery(() => ({
      queryKey: ['todos'],
      queryFn: async () => {
        const response = await fetch('https://api.example.com/todos')
        return response.json()
      },
    }))

    return (
      <Show when={todosQuery.data}>
        {(todos) => (
          <For each={todos()}>
            {(todo) => <div>{todo.title}</div>}
          </For>
        )}
      </Show>
    )
  }
  ```

  <Tip>
    The `queryKey` uniquely identifies your query. It's used for caching, refetching, and more.
  </Tip>

  ### Add a Mutation

  Use `useMutation` to modify data:

  ```tsx theme={null}
  import { useMutation, useQueryClient } from '@tanstack/solid-query'

  function AddTodo() {
    const queryClient = useQueryClient()
    
    const mutation = useMutation(() => ({
      mutationFn: async (newTodo: { title: string }) => {
        const response = await fetch('https://api.example.com/todos', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(newTodo),
        })
        return response.json()
      },
      onSuccess: () => {
        // Invalidate and refetch
        queryClient.invalidateQueries({ queryKey: ['todos'] })
      },
    }))

    const handleSubmit = (e: Event) => {
      e.preventDefault()
      const formData = new FormData(e.target as HTMLFormElement)
      const title = formData.get('title') as string
      mutation.mutate({ title })
    }

    return (
      <form onSubmit={handleSubmit}>
        <input name="title" required />
        <button type="submit" disabled={mutation.isPending}>
          {mutation.isPending ? 'Adding...' : 'Add Todo'}
        </button>
      </form>
    )
  }
  ```

  ### Handle Loading and Error States

  Solid Query provides reactive state for all query states:

  ```tsx theme={null}
  function TodoList() {
    const todosQuery = useQuery(() => ({
      queryKey: ['todos'],
      queryFn: fetchTodos,
    }))

    return (
      <div>
        <Show when={todosQuery.isLoading}>
          <div>Loading todos...</div>
        </Show>
        
        <Show when={todosQuery.isError}>
          <div>Error: {todosQuery.error?.message}</div>
        </Show>
        
        <Show when={todosQuery.isSuccess}>
          <Show when={todosQuery.data}>
            {(todos) => (
              <For each={todos()}>
                {(todo) => <TodoItem todo={todo} />}
              </For>
            )}
          </Show>
        </Show>

        <Show when={todosQuery.isFetching}>
          <div>Refreshing...</div>
        </Show>
      </div>
    )
  }
  ```
</Steps>

## Query Keys

Query keys are how Solid Query identifies and caches your data. They can be simple strings or arrays:

```tsx theme={null}
// Simple key
const query1 = useQuery(() => ({
  queryKey: ['todos'],
  queryFn: fetchTodos,
}))

// Key with parameters
const query2 = useQuery(() => ({
  queryKey: ['todo', props.todoId],
  queryFn: () => fetchTodo(props.todoId),
}))

// Complex key with filters
const query3 = useQuery(() => ({
  queryKey: ['todos', { status: 'done', page: 1 }],
  queryFn: () => fetchTodos({ status: 'done', page: 1 }),
}))
```

<Warning>
  Query keys must be serializable and should include all variables that affect the query function.
</Warning>

## Working with Suspense

Solid Query integrates seamlessly with SolidJS Suspense:

```tsx theme={null}
import { Suspense } from 'solid-js'
import { useQuery } from '@tanstack/solid-query'

function UserProfile(props: { userId: string }) {
  const userQuery = useQuery(() => ({
    queryKey: ['user', props.userId],
    queryFn: () => fetchUser(props.userId),
  }))

  // Accessing .data will suspend automatically
  return (
    <div>
      <h2>{userQuery.data.name}</h2>
      <p>{userQuery.data.email}</p>
    </div>
  )
}

function App() {
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <UserProfile userId="123" />
    </Suspense>
  )
}
```

<Tip>
  The `data` property is a SolidJS Resource that automatically suspends when the query is loading.
</Tip>

## Dependent Queries

Sometimes you need to fetch data that depends on other data:

```tsx theme={null}
function UserPosts(props: { userId: string }) {
  // First query: fetch user
  const userQuery = useQuery(() => ({
    queryKey: ['user', props.userId],
    queryFn: () => fetchUser(props.userId),
  }))

  // Second query: fetch posts (depends on user)
  const postsQuery = useQuery(() => ({
    queryKey: ['posts', props.userId],
    queryFn: () => fetchUserPosts(props.userId),
    // Only run when we have the user data
    enabled: !!userQuery.data,
  }))

  return (
    <div>
      <Show when={userQuery.data}>
        {(user) => (
          <div>
            <h2>{user().name}'s Posts</h2>
            <Show when={postsQuery.data}>
              {(posts) => (
                <For each={posts()}>
                  {(post) => <div>{post.title}</div>}
                </For>
              )}
            </Show>
          </div>
        )}
      </Show>
    </div>
  )
}
```

## Pagination Example

Handle paginated data with ease:

```tsx theme={null}
import { createSignal } from 'solid-js'
import { useQuery } from '@tanstack/solid-query'

function PaginatedPosts() {
  const [page, setPage] = createSignal(1)

  const postsQuery = useQuery(() => ({
    queryKey: ['posts', page()],
    queryFn: () => fetchPosts(page()),
    // Keep previous data while fetching new page
    placeholderData: (previousData) => previousData,
  }))

  return (
    <div>
      <Show when={postsQuery.data}>
        {(posts) => (
          <For each={posts()}>
            {(post) => <div>{post.title}</div>}
          </For>
        )}
      </Show>
      
      <div>
        <button
          onClick={() => setPage((p) => Math.max(1, p - 1))}
          disabled={page() === 1}
        >
          Previous
        </button>
        <span>Page {page()}</span>
        <button
          onClick={() => setPage((p) => p + 1)}
          disabled={postsQuery.data?.length === 0}
        >
          Next
        </button>
      </div>
      
      <Show when={postsQuery.isFetching}>
        <div>Loading...</div>
      </Show>
    </div>
  )
}
```

## Infinite Queries

For infinite scroll or "load more" functionality:

```tsx theme={null}
import { useInfiniteQuery } from '@tanstack/solid-query'

function InfinitePosts() {
  const query = useInfiniteQuery(() => ({
    queryKey: ['posts'],
    queryFn: async ({ pageParam = 0 }) => {
      const response = await fetch(`/api/posts?cursor=${pageParam}`)
      return response.json()
    },
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    getPreviousPageParam: (firstPage) => firstPage.previousCursor,
  }))

  return (
    <div>
      <For each={query.data?.pages}>
        {(page) => (
          <For each={page.posts}>
            {(post) => <div>{post.title}</div>}
          </For>
        )}
      </For>
      
      <button
        onClick={() => query.fetchNextPage()}
        disabled={!query.hasNextPage || query.isFetchingNextPage}
      >
        {query.isFetchingNextPage
          ? 'Loading more...'
          : query.hasNextPage
          ? 'Load More'
          : 'Nothing more to load'}
      </button>
    </div>
  )
}
```

## Optimistic Updates

Update the UI immediately while the mutation is in progress:

```tsx theme={null}
function TodoList() {
  const queryClient = useQueryClient()
  
  const updateMutation = useMutation(() => ({
    mutationFn: (updatedTodo: Todo) => updateTodo(updatedTodo),
    // Optimistic update
    onMutate: async (updatedTodo) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] })
      
      // Snapshot the previous value
      const previousTodos = queryClient.getQueryData(['todos'])
      
      // Optimistically update to the new value
      queryClient.setQueryData(['todos'], (old: Todo[]) =>
        old.map((todo) => todo.id === updatedTodo.id ? updatedTodo : todo)
      )
      
      // Return context with the snapshot
      return { previousTodos }
    },
    // If mutation fails, roll back
    onError: (err, updatedTodo, context) => {
      queryClient.setQueryData(['todos'], context?.previousTodos)
    },
    // Always refetch after error or success
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  }))

  return <div>{/* Your component */}</div>
}
```

## Best Practices

<Note>
  Follow these best practices for optimal performance and maintainability:
</Note>

1. **Use Query Keys Consistently**: Keep your query keys organized and consistent across your application

2. **Set Appropriate Stale Time**: Configure `staleTime` based on how often your data changes

3. **Handle All States**: Always handle loading, error, and success states

4. **Use Query Options Helper**: Create reusable query configurations with `queryOptions`

5. **Leverage Suspense**: Use Suspense boundaries for better loading experiences

6. **Invalidate Wisely**: Invalidate queries after mutations to keep data fresh

## Next Steps

<CardGroup cols={2}>
  <Card title="TypeScript" icon="code" href="/frameworks/solid/typescript">
    Learn about TypeScript integration and type safety
  </Card>

  <Card title="DevTools" icon="wrench" href="/frameworks/solid/devtools">
    Debug your queries with Solid Query DevTools
  </Card>
</CardGroup>
