Elysia Tools

Browse tools, examples, visual explainers, and topic guides from one clear workspace.

Top Categories

Hubs

Language
ZHESPTRUFRDEEN

Elysia Tools

Navigation

Data Fetching

TanStack Query Samples

TanStack Query (formerly React Query) examples including data fetching, caching, mutations, optimistic updates, and advanced patterns

Samples

Entries inside this sample collection

CodetypescriptSimple

TanStack Query Basic Usage

Fundamental TanStack Query patterns including queries, mutations, error handling, and loading states

Difficulty

3/10

Estimated time

25 min

Tags

tanstack-query, react, data-fetching, state-management

Prerequisites

React hooks, TypeScript, Async/await, REST APIs

typescript
// TanStack Query Basic Usage Examples
// TanStack Query is a powerful data fetching and state management library for React

import { useQuery, useMutation, useQueryClient, QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState, useEffect, useCallback, useMemo } from 'react'

// 1. Setup Query Client
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      retry: 3,
      refetchOnWindowFocus: false,
    },
  },
})

// Provider component
export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UserProfile />
      <TodoList />
      <PostList />
    </QueryClientProvider>
  )
}

// 2. Basic Data Fetching with useQuery
interface User {
  id: number
  name: string
  email: string
  username: string
  address: {
    street: string
    city: string
    zipcode: string
  }
}

// Fetch function
const fetchUser = async (id: number): Promise<User> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)

  if (!response.ok) {
    throw new Error('Failed to fetch user')
  }

  return response.json()
}

// User component with useQuery
function UserProfile() {
  const [userId, setUserId] = useState(1)

  const {
    data: user,
    isLoading,
    isError,
    error,
    refetch,
    isFetching,
  } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    enabled: !!userId, // Only fetch when userId exists
    staleTime: 1000 * 60 * 10, // 10 minutes
  })

  const handleNextUser = () => {
    setUserId(prev => prev + 1)
  }

  const handlePrevUser = () => {
    setUserId(prev => Math.max(1, prev - 1))
  }

  return (
    <div className="p-6 border rounded-lg mb-6">
      <h2 className="text-2xl font-bold mb-4">User Profile</h2>

      <div className="mb-4 space-x-2">
        <button
          onClick={handlePrevUser}
          disabled={userId <= 1}
          className="px-3 py-1 bg-gray-500 text-white rounded disabled:opacity-50"
        >
          Previous
        </button>
        <span className="mx-2">User ID: {userId}</span>
        <button
          onClick={handleNextUser}
          className="px-3 py-1 bg-blue-500 text-white rounded"
        >
          Next
        </button>
        <button
          onClick={() => refetch()}
          disabled={isFetching}
          className="px-3 py-1 bg-green-500 text-white rounded disabled:opacity-50 ml-4"
        >
          {isFetching ? 'Refetching...' : 'Refetch'}
        </button>
      </div>

      {isLoading && <div className="text-blue-500">Loading user data...</div>}

      {isError && (
        <div className="text-red-500">
          Error: {error instanceof Error ? error.message : 'Unknown error'}
        </div>
      )}

      {user && (
        <div>
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">@{user.username}</p>
          <p className="text-gray-800">{user.email}</p>
          <p className="text-gray-700">
            {user.address.street}, {user.address.city} {user.address.zipcode}
          </p>
        </div>
      )}
    </div>
  )
}

// 3. Mutations with useMutation
interface Todo {
  id: number
  title: string
  completed: boolean
  userId: number
}

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

const createTodo = async (newTodo: { title: string; userId: number }): Promise<Todo> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      title: newTodo.title,
      userId: newTodo.userId,
      completed: false,
    }),
  })

  if (!response.ok) throw new Error('Failed to create todo')
  return response.json()
}

const toggleTodo = async (todoId: number, completed: boolean): Promise<Todo> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`, {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ completed }),
  })

  if (!response.ok) throw new Error('Failed to update todo')
  return response.json()
}

function TodoList() {
  const queryClient = useQueryClient()
  const [newTodoTitle, setNewTodoTitle] = useState('')

  // Fetch todos
  const {
    data: todos = [],
    isLoading,
    isError,
    error,
  } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    staleTime: 1000 * 60 * 5, // 5 minutes
  })

  // Create todo mutation
  const createTodoMutation = useMutation({
    mutationFn: createTodo,
    onSuccess: (newTodo) => {
      // Add new todo to cache
      queryClient.setQueryData(['todos'], (oldTodos: Todo[] = []) => [
        ...oldTodos,
        newTodo,
      ])
      setNewTodoTitle('') // Clear input
    },
    onError: (error) => {
      console.error('Failed to create todo:', error)
    },
  })

  // Toggle todo mutation
  const toggleTodoMutation = useMutation({
    mutationFn: ({ id, completed }: { id: number; completed: boolean }) =>
      toggleTodo(id, completed),
    onSuccess: (updatedTodo) => {
      // Update todo in cache
      queryClient.setQueryData(['todos'], (oldTodos: Todo[] = []) =>
        oldTodos.map(todo =>
          todo.id === updatedTodo.id ? updatedTodo : todo
        )
      )
    },
  })

  const handleCreateTodo = (e: React.FormEvent) => {
    e.preventDefault()
    if (newTodoTitle.trim()) {
      createTodoMutation.mutate({
        title: newTodoTitle.trim(),
        userId: 1, // Using fixed user ID for demo
      })
    }
  }

  const handleToggleTodo = (todoId: number, completed: boolean) => {
    toggleTodoMutation.mutate({ id: todoId, completed: !completed })
  }

  if (isLoading) return <div className="p-4">Loading todos...</div>
  if (isError) return <div className="p-4 text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>

  return (
    <div className="p-6 border rounded-lg mb-6">
      <h2 className="text-2xl font-bold mb-4">Todo List</h2>

      <form onSubmit={handleCreateTodo} className="mb-4 flex gap-2">
        <input
          type="text"
          value={newTodoTitle}
          onChange={(e) => setNewTodoTitle(e.target.value)}
          placeholder="New todo title..."
          className="flex-1 px-3 py-2 border rounded"
          disabled={createTodoMutation.isPending}
        />
        <button
          type="submit"
          disabled={createTodoMutation.isPending}
          className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {createTodoMutation.isPending ? 'Adding...' : 'Add Todo'}
        </button>
      </form>

      <ul className="space-y-2">
        {todos.slice(0, 10).map((todo) => (
          <li key={todo.id} className="flex items-center gap-2 p-2 border rounded">
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(todo.id, todo.completed)}
              disabled={toggleTodoMutation.isPending}
              className="w-4 h-4"
            />
            <span className={todo.completed ? 'line-through text-gray-500' : ''}>
              {todo.title}
            </span>
            {toggleTodoMutation.isPending && (
              <span className="text-sm text-gray-500">Updating...</span>
            )}
          </li>
        ))}
      </ul>
    </div>
  )
}

// 4. Dependent Queries
interface Post {
  id: number
  title: string
  body: string
  userId: number
}

interface Comment {
  id: number
  postId: number
  name: string
  email: string
  body: string
}

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

const fetchComments = async (postId: number): Promise<Comment[]> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}/comments`)
  if (!response.ok) throw new Error('Failed to fetch comments')
  return response.json()
}

function PostList() {
  const [selectedUserId] = useState(1)

  // First query: fetch posts for a user
  const {
    data: posts = [],
    isLoading: postsLoading,
    error: postsError,
  } = useQuery({
    queryKey: ['posts', selectedUserId],
    queryFn: () => fetchPosts(selectedUserId),
    enabled: !!selectedUserId,
  })

  // Second query: fetch comments for the first post (dependent on first query)
  const {
    data: comments = [],
    isLoading: commentsLoading,
    error: commentsError,
  } = useQuery({
    queryKey: ['comments', posts[0]?.id],
    queryFn: () => fetchComments(posts[0]?.id),
    enabled: !!posts[0]?.id, // Only run when we have a post ID
  })

  if (postsLoading) return <div className="p-4">Loading posts...</div>
  if (postsError) return <div className="p-4 text-red-500">Error loading posts</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Posts and Comments</h2>

      <div className="mb-6">
        <h3 className="text-xl font-semibold mb-3">User Posts</h3>
        <div className="space-y-3">
          {posts.slice(0, 3).map((post) => (
            <div key={post.id} className="p-3 border rounded">
              <h4 className="font-medium">{post.title}</h4>
              <p className="text-gray-600 text-sm mt-1">{post.body.slice(0, 100)}...</p>
            </div>
          ))}
        </div>
      </div>

      {commentsLoading ? (
        <div>Loading comments...</div>
      ) : commentsError ? (
        <div className="text-red-500">Error loading comments</div>
      ) : (
        <div>
          <h3 className="text-xl font-semibold mb-3">Comments for First Post</h3>
          <div className="space-y-2">
            {comments.slice(0, 3).map((comment) => (
              <div key={comment.id} className="p-3 bg-gray-50 rounded">
                <div className="font-medium">{comment.name}</div>
                <div className="text-sm text-gray-600">{comment.email}</div>
                <div className="text-sm mt-1">{comment.body}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  )
}

// 5. Pagination with Query Keys
function PaginatedComponent() {
  const [page, setPage] = useState(1)
  const pageSize = 10

  const {
    data,
    isLoading,
    isError,
    error,
  } = useQuery({
    queryKey: ['items', page, pageSize],
    queryFn: async () => {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/todos?_page=${page}&_limit=${pageSize}`
      )
      if (!response.ok) throw new Error('Failed to fetch data')
      return response.json()
    },
    keepPreviousData: true, // Keep previous data while loading new page
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Paginated Todos</h2>

      {isLoading && <div>Loading...</div>}
      {isError && <div className="text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>}

      {data && (
        <>
          <ul className="space-y-2 mb-4">
            {data.map((item: any) => (
              <li key={item.id} className="p-2 border rounded">
                {item.title}
              </li>
            ))}
          </ul>

          <div className="flex gap-2">
            <button
              onClick={() => setPage(prev => Math.max(1, prev - 1))}
              disabled={page === 1}
              className="px-3 py-1 bg-gray-500 text-white rounded disabled:opacity-50"
            >
              Previous
            </button>
            <span className="px-3 py-1">Page {page}</span>
            <button
              onClick={() => setPage(prev => prev + 1)}
              className="px-3 py-1 bg-blue-500 text-white rounded"
            >
              Next
            </button>
          </div>
        </>
      )}
    </div>
  )
}

export { App, UserProfile, TodoList, PostList, PaginatedComponent }
CodetypescriptIntermediate

TanStack Query Advanced Patterns

Advanced TanStack Query patterns including optimistic updates, infinite queries, background refetching, and custom hooks

Difficulty

7/10

Estimated time

45 min

Tags

tanstack-query, advanced, optimistic, infinite, realtime

Prerequisites

TanStack Query basics, React hooks, TypeScript, Advanced patterns

typescript
// TanStack Query Advanced Patterns
// Advanced data fetching patterns and optimizations

import {
  useQuery,
  useMutation,
  useQueryClient,
  useInfiniteQuery,
  QueryClient,
  QueryClientProvider,
  useQueries,
  QueryObserver,
} from '@tanstack/react-query'
import { useState, useEffect } from 'react'

// 1. Optimistic Updates
interface Todo {
  id: number
  title: string
  completed: boolean
  userId: number
  createdAt: string
}

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

const updateTodo = async (id: number, updates: Partial<Todo>): Promise<Todo> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(updates),
  })
  if (!response.ok) throw new Error('Failed to update todo')
  return response.json()
}

const deleteTodo = async (id: number): Promise<void> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
    method: 'DELETE',
  })
  if (!response.ok) throw new Error('Failed to delete todo')
}

function OptimisticTodoList() {
  const queryClient = useQueryClient()

  const {
    data: todos = [],
    isLoading,
  } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  })

  const updateTodoMutation = useMutation({
    mutationFn: ({ id, updates }: { id: number; updates: Partial<Todo> }) =>
      updateTodo(id, updates),
    onMutate: async ({ id, updates }) => {
      // Cancel any outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] })

      // Snapshot the previous value
      const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])

      // Optimistically update to the new value
      queryClient.setQueryData<Todo[]>(['todos'], (old = []) =>
        old.map(todo =>
          todo.id === id ? { ...todo, ...updates } : todo
        )
      )

      // Return a context object with the snapshotted value
      return { previousTodos }
    },
    onError: (error, variables, context) => {
      // If the mutation fails, use the context returned from onMutate
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos)
      }
    },
    onSettled: () => {
      // Always refetch after error or success
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  const deleteTodoMutation = useMutation({
    mutationFn: deleteTodo,
    onMutate: async (id) => {
      await queryClient.cancelQueries({ queryKey: ['todos'] })
      const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])

      queryClient.setQueryData<Todo[]>(['todos'], (old = []) =>
        old.filter(todo => todo.id !== id)
      )

      return { previousTodos }
    },
    onError: (error, id, context) => {
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  const handleToggleTodo = (todo: Todo) => {
    updateTodoMutation.mutate({
      id: todo.id,
      updates: { completed: !todo.completed },
    })
  }

  const handleDeleteTodo = (id: number) => {
    deleteTodoMutation.mutate(id)
  }

  if (isLoading) return <div>Loading...</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Optimistic Todo List</h2>
      <p className="text-gray-600 mb-4">Changes appear instantly and rollback on errors</p>

      <ul className="space-y-2">
        {todos.slice(0, 10).map((todo) => (
          <li
            key={todo.id}
            className={`flex items-center gap-2 p-3 border rounded ${
              updateTodoMutation.isPending && updateTodoMutation.variables?.id === todo.id
                ? 'opacity-50'
                : ''
            }`}
          >
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(todo)}
              disabled={updateTodoMutation.isPending}
              className="w-4 h-4"
            />
            <span className={todo.completed ? 'line-through text-gray-500' : ''}>
              {todo.title}
            </span>
            <button
              onClick={() => handleDeleteTodo(todo.id)}
              disabled={deleteTodoMutation.isPending}
              className="ml-auto px-2 py-1 text-sm bg-red-500 text-white rounded"
            >
              {deleteTodoMutation.isPending ? 'Deleting...' : 'Delete'}
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}

// 2. Infinite Scroll with useInfiniteQuery
interface Post {
  id: number
  title: string
  body: string
  userId: number
}

const fetchPostsPage = async ({ pageParam = 1 }): Promise<{
  data: Post[]
  page: number
  nextPage: number | null
}> => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/posts?_page=${pageParam}&_limit=10`
  )

  if (!response.ok) throw new Error('Failed to fetch posts')

  const posts = await response.json()
  return {
    data: posts,
    page: pageParam,
    nextPage: posts.length === 10 ? pageParam + 1 : null,
  }
}

function InfinitePostList() {
  const {
    data,
    isLoading,
    isError,
    error,
    hasNextPage,
    isFetchingNextPage,
    fetchNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts-infinite'],
    queryFn: fetchPostsPage,
    getNextPageParam: (lastPage) => lastPage.nextPage,
    initialPageParam: 1,
  })

  const posts = data?.pages.flatMap(page => page.data) ?? []

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Infinite Scroll Posts</h2>

      {isLoading && <div>Loading initial posts...</div>}
      {isError && <div className="text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>}

      <div className="space-y-4">
        {posts.map((post) => (
          <div key={post.id} className="p-4 border rounded">
            <h3 className="font-semibold text-lg">{post.title}</h3>
            <p className="text-gray-600 mt-2">{post.body}</p>
          </div>
        ))}
      </div>

      <div className="mt-6 text-center">
        <button
          onClick={() => fetchNextPage()}
          disabled={!hasNextPage || isFetchingNextPage}
          className="px-6 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {isFetchingNextPage
            ? 'Loading more...'
            : hasNextPage
            ? 'Load More'
            : 'No more posts'
          }
        </button>
      </div>
    </div>
  )
}

// 3. Multiple Queries with useQueries
function UserDashboard() {
  const [userId] = useState(1)

  const results = useQueries({
    queries: [
      {
        queryKey: ['user', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
          if (!response.ok) throw new Error('Failed to fetch user')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
      {
        queryKey: ['posts', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`)
          if (!response.ok) throw new Error('Failed to fetch posts')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
      {
        queryKey: ['todos', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/todos`)
          if (!response.ok) throw new Error('Failed to fetch todos')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
    ],
  })

  const [userResult, postsResult, todosResult] = results
  const isLoading = results.some(result => result.isLoading)
  const isError = results.some(result => result.isError)

  if (isLoading) return <div className="p-6">Loading dashboard...</div>
  if (isError) return <div className="p-6 text-red-500">Error loading dashboard data</div>

  const user = userResult.data
  const posts = postsResult.data ?? []
  const todos = todosResult.data ?? []

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">User Dashboard</h2>

      {user && (
        <div className="mb-6">
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">@{user.username}</p>
          <p className="text-gray-800">{user.email}</p>
        </div>
      )}

      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        <div>
          <h4 className="font-semibold mb-3">Recent Posts ({posts.length})</h4>
          <div className="space-y-2">
            {posts.slice(0, 3).map((post: any) => (
              <div key={post.id} className="p-3 bg-gray-50 rounded">
                <h5 className="font-medium">{post.title}</h5>
                <p className="text-sm text-gray-600 mt-1">{post.body.slice(0, 80)}...</p>
              </div>
            ))}
          </div>
        </div>

        <div>
          <h4 className="font-semibold mb-3">Todo Status</h4>
          <div className="space-y-2">
            <div className="flex justify-between p-3 bg-green-50 rounded">
              <span>Completed</span>
              <span className="font-medium">
                {todos.filter((todo: any) => todo.completed).length}
              </span>
            </div>
            <div className="flex justify-between p-3 bg-yellow-50 rounded">
              <span>Pending</span>
              <span className="font-medium">
                {todos.filter((todo: any) => !todo.completed).length}
              </span>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

// 4. Custom Hook Pattern
// Create a custom hook for user-related queries
function useUser(userId: number) {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      if (!response.ok) throw new Error('Failed to fetch user')
      return response.json()
    },
    enabled: !!userId,
    staleTime: 1000 * 60 * 10, // 10 minutes
  })
}

function useUserPosts(userId: number) {
  return useQuery({
    queryKey: ['user-posts', userId],
    queryFn: async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`)
      if (!response.ok) throw new Error('Failed to fetch user posts')
      return response.json()
    },
    enabled: !!userId,
    staleTime: 1000 * 60 * 5, // 5 minutes
  })
}

function useCreatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async (newPost: { title: string; body: string; userId: number }) => {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newPost),
      })
      if (!response.ok) throw new Error('Failed to create post')
      return response.json()
    },
    onSuccess: (newPost, variables) => {
      // Invalidate user posts query to refetch
      queryClient.invalidateQueries({
        queryKey: ['user-posts', variables.userId]
      })
    },
  })
}

// Component using custom hooks
function UserProfileWithPosts() {
  const [userId] = useState(1)
  const [showCreateForm, setShowCreateForm] = useState(false)

  const { data: user, isLoading: userLoading, error: userError } = useUser(userId)
  const { data: posts, isLoading: postsLoading, error: postsError } = useUserPosts(userId)
  const createPostMutation = useCreatePost()

  if (userLoading || postsLoading) return <div>Loading...</div>
  if (userError || postsError) return <div>Error loading data</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">User Profile with Posts</h2>

      {user && (
        <div className="mb-6">
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">{user.email}</p>
        </div>
      )}

      <div className="mb-4">
        <button
          onClick={() => setShowCreateForm(!showCreateForm)}
          className="px-4 py-2 bg-blue-500 text-white rounded"
        >
          {showCreateForm ? 'Hide Form' : 'Create New Post'}
        </button>
      </div>

      {showCreateForm && (
        <form
          onSubmit={(e) => {
            e.preventDefault()
            const formData = new FormData(e.currentTarget)
            createPostMutation.mutate({
              title: formData.get('title') as string,
              body: formData.get('body') as string,
              userId,
            })
          }}
          className="mb-6 p-4 border rounded bg-gray-50"
        >
          <div className="mb-3">
            <label className="block text-sm font-medium mb-1">Title</label>
            <input
              name="title"
              type="text"
              required
              className="w-full px-3 py-2 border rounded"
              disabled={createPostMutation.isPending}
            />
          </div>
          <div className="mb-3">
            <label className="block text-sm font-medium mb-1">Body</label>
            <textarea
              name="body"
              required
              rows={3}
              className="w-full px-3 py-2 border rounded"
              disabled={createPostMutation.isPending}
            />
          </div>
          <button
            type="submit"
            disabled={createPostMutation.isPending}
            className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
          >
            {createPostMutation.isPending ? 'Creating...' : 'Create Post'}
          </button>
        </form>
      )}

      <div>
        <h4 className="font-semibold mb-3">Posts ({posts?.length || 0})</h4>
        <div className="space-y-3">
          {posts?.map((post: any) => (
            <div key={post.id} className="p-3 border rounded">
              <h5 className="font-medium">{post.title}</h5>
              <p className="text-gray-600 text-sm mt-1">{post.body}</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

// 5. Background Refetching and Window Focus
function BackgroundRefetchExample() {
  const [enabled, setEnabled] = useState(true)
  const [refetchInterval, setRefetchInterval] = useState(5000) // 5 seconds

  const {
    data: todos = [],
    isLoading,
    error,
  } = useQuery({
    queryKey: ['todos', 'background'],
    queryFn: fetchTodos,
    refetchInterval: enabled ? refetchInterval : false,
    refetchIntervalInBackground: true,
    refetchOnWindowFocus: enabled,
    staleTime: 1000 * 30, // 30 seconds
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Background Refetching</h2>

      <div className="mb-4 space-y-2">
        <label className="flex items-center gap-2">
          <input
            type="checkbox"
            checked={enabled}
            onChange={(e) => setEnabled(e.target.checked)}
          />
          <span>Enable background refetching</span>
        </label>

        <div>
          <label className="block text-sm font-medium mb-1">
            Refetch Interval (ms): {refetchInterval}
          </label>
          <input
            type="range"
            min="1000"
            max="30000"
            step="1000"
            value={refetchInterval}
            onChange={(e) => setRefetchInterval(Number(e.target.value))}
            className="w-full"
          />
        </div>
      </div>

      {isLoading && <div>Loading...</div>}
      {error && <div className="text-red-500">Error loading data</div>}

      <div>
        <p className="text-sm text-gray-600 mb-2">
          Last updated: {new Date().toLocaleTimeString()}
        </p>
        <div className="text-sm text-gray-600 mb-4">
          Total todos: {todos.length}
        </div>

        <div className="space-y-2">
          {todos.slice(0, 5).map((todo: any) => (
            <div key={todo.id} className="flex items-center gap-2 p-2 border rounded">
              <input
                type="checkbox"
                checked={todo.completed}
                readOnly
                className="w-4 h-4"
              />
              <span className={todo.completed ? 'line-through text-gray-500' : ''}>
                {todo.title}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

// 6. Query Observer for Manual Subscriptions
function useRealTimeData(queryKey: string[], queryFn: () => Promise<any>) {
  const [data, setData] = useState<any>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  useEffect(() => {
    const queryClient = new QueryClient()

    const observer = new QueryObserver(queryClient, {
      queryKey,
      queryFn,
      refetchInterval: 5000, // Refetch every 5 seconds
    })

    const subscription = observer.subscribe((result) => {
      setLoading(result.isLoading)
      setError(result.error as Error | null)
      setData(result.data)
    })

    return () => {
      subscription.unsubscribe()
    }
  }, [queryKey.toString()])

  return { data, loading, error }
}

function RealTimeDataComponent() {
  const { data, loading, error } = useRealTimeData(
    ['realtime-todos'],
    fetchTodos
  )

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Real-time Data</h2>
      <p className="text-gray-600 mb-4">Updates every 5 seconds</p>

      {loading && <div>Loading...</div>}
      {error && <div className="text-red-500">Error: {error.message}</div>}

      {data && (
        <div>
          <div className="text-sm text-gray-600 mb-2">
            Total todos: {data.length}
          </div>
          <div className="text-sm text-green-600 mb-4">
            Last update: {new Date().toLocaleTimeString()}
          </div>
        </div>
      )}
    </div>
  )
}

export {
  OptimisticTodoList,
  InfinitePostList,
  UserDashboard,
  UserProfileWithPosts,
  BackgroundRefetchExample,
  RealTimeDataComponent,
}
CodetypescriptComplex

TanStack Query Data Management

Complete data management patterns including caching strategies, data synchronization, error boundaries, and performance optimization

Difficulty

9/10

Estimated time

60 min

Tags

tanstack-query, data-management, performance, optimization

Prerequisites

Advanced TanStack Query, React patterns, TypeScript, State management

typescript
// TanStack Query Advanced Data Management
// Comprehensive data management patterns and optimizations

import {
  useQuery,
  useMutation,
  useQueryClient,
  QueryClient,
  QueryClientProvider,
  useQueries,
  QueryObserver,
  QueryCache,
  MutationCache,
} from '@tanstack/react-query'
import { useState, useEffect, useCallback, useMemo, createContext, useContext } from 'react'

// 1. Advanced Query Client Configuration
const createAdvancedQueryClient = () => {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60 * 5, // 5 minutes
        cacheTime: 1000 * 60 * 10, // 10 minutes
        retry: (failureCount, error) => {
          // Don't retry on 4xx errors
          if (error && typeof error === 'object' && 'status' in error) {
            const status = (error as any).status
            if (status >= 400 && status < 500) {
              return false
            }
          }
          return failureCount < 3
        },
        retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
        refetchOnWindowFocus: false,
        refetchOnReconnect: true,
        notifyOnChangeProps: ['data', 'error', 'isLoading'],
      },
      mutations: {
        retry: 1,
        onMutate: () => {
          // Show loading indicator globally
        },
        onSuccess: () => {
          // Show success notification
        },
        onError: () => {
          // Show error notification
        },
      },
    },
    queryCache: new QueryCache({
      onError: (error, query) => {
        // Global query error handler
        console.error('Query error:', error, 'Query:', query.queryKey)
      },
      onSuccess: (data, query) => {
        // Global query success handler
        console.log('Query success:', query.queryKey)
      },
    }),
    mutationCache: new MutationCache({
      onError: (error, variables, context, mutation) => {
        // Global mutation error handler
        console.error('Mutation error:', error)
      },
    }),
  })
}

// 2. Data Context for Global State Management
interface DataContextType {
  invalidateQueries: (queryKey: string[]) => void
  setQueryData: (queryKey: string[], data: any) => void
  getQueryData: (queryKey: string[]) => any
  prefetchQuery: (queryKey: string[], queryFn: () => Promise<any>) => void
}

const DataContext = createContext<DataDataContextType | null>(null)

export function DataProvider({ children }: { children: React.ReactNode }) {
  const queryClient = useQueryClient()

  const invalidateQueries = useCallback((queryKey: string[]) => {
    queryClient.invalidateQueries({ queryKey })
  }, [queryClient])

  const setQueryData = useCallback((queryKey: string[], data: any) => {
    queryClient.setQueryData(queryKey, data)
  }, [queryClient])

  const getQueryData = useCallback((queryKey: string[]) => {
    return queryClient.getQueryData(queryKey)
  }, [queryClient])

  const prefetchQuery = useCallback((queryKey: string[], queryFn: () => Promise<any>) => {
    queryClient.prefetchQuery({ queryKey, queryFn })
  }, [queryClient])

  const value = useMemo(() => ({
    invalidateQueries,
    setQueryData,
    getQueryData,
    prefetchQuery,
  }), [invalidateQueries, setQueryData, getQueryData, prefetchQuery])

  return (
    <DataContext.Provider value={value}>
      {children}
    </DataContext.Provider>
  )
}

export function useDataContext() {
  const context = useContext(DataContext)
  if (!context) {
    throw new Error('useDataContext must be used within a DataProvider')
  }
  return context
}

// 3. Smart Data Fetching Hook with Caching
interface UseSmartFetchOptions {
  enabled?: boolean
  staleTime?: number
  cacheTime?: number
  refetchOnWindowFocus?: boolean
  retry?: boolean | number
}

function useSmartFetch<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: UseSmartFetchOptions = {}
) {
  const {
    enabled = true,
    staleTime = 1000 * 60 * 5,
    cacheTime = 1000 * 60 * 10,
    refetchOnWindowFocus = false,
    retry = 3,
  } = options

  return useQuery({
    queryKey,
    queryFn,
    enabled,
    staleTime,
    cacheTime,
    refetchOnWindowFocus,
    retry,
    select: useCallback((data: T) => {
      // Transform data if needed
      return data
    }, []),
  })
}

// 4. Offline-aware Data Management
function useOfflineAwareQuery<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: any = {}
) {
  const [isOnline, setIsOnline] = useState(navigator.onLine)

  useEffect(() => {
    const handleOnline = () => setIsOnline(true)
    const handleOffline = () => setIsOnline(false)

    window.addEventListener('online', handleOnline)
    window.addEventListener('offline', handleOffline)

    return () => {
      window.removeEventListener('online', handleOnline)
      window.removeEventListener('offline', handleOffline)
    }
  }, [])

  return useQuery({
    queryKey,
    queryFn,
    enabled: isOnline && (options.enabled ?? true),
    retry: (failureCount, error) => {
      // Retry more aggressively when coming back online
      if (isOnline && failureCount < 5) {
        return true
      }
      return false
    },
    refetchOnWindowFocus: isOnline,
    ...options,
  })
}

// 5. Data Synchronization Patterns
class DataSyncManager {
  private queryClient: QueryClient
  private syncQueue: Array<() => Promise<void>> = []
  private isSyncing = false

  constructor(queryClient: QueryClient) {
    this.queryClient = queryClient
    this.setupOnlineListener()
  }

  private setupOnlineListener() {
    window.addEventListener('online', () => {
      this.processSyncQueue()
    })
  }

  addToSyncQueue(syncFunction: () => Promise<void>) {
    this.syncQueue.push(syncFunction)
    if (navigator.onLine && !this.isSyncing) {
      this.processSyncQueue()
    }
  }

  private async processSyncQueue() {
    if (this.isSyncing || this.syncQueue.length === 0) return

    this.isSyncing = true
    console.log('Starting sync process...')

    while (this.syncQueue.length > 0) {
      const syncFunction = this.syncQueue.shift()
      try {
        await syncFunction()
      } catch (error) {
        console.error('Sync operation failed:', error)
        // Re-queue failed operations
        if (syncFunction) {
          this.syncQueue.push(syncFunction)
        }
      }
    }

    this.isSyncing = false
    console.log('Sync process completed')
  }

  async forceSync() {
    await this.processSyncQueue()
  }
}

// 6. Cache Management Utilities
function useCacheManager() {
  const queryClient = useQueryClient()

  const clearCache = useCallback(() => {
    queryClient.clear()
  }, [queryClient])

  const clearSpecificCache = useCallback((queryKey: string[]) => {
    queryClient.removeQueries({ queryKey })
  }, [queryClient])

  const getCacheSize = useCallback(() => {
    return queryClient.getQueryCache().getAll().length
  }, [queryClient])

  const prefetchMultipleQueries = useCallback((queries: Array<{
    queryKey: string[]
    queryFn: () => Promise<any>
  }>) => {
    queries.forEach(({ queryKey, queryFn }) => {
      queryClient.prefetchQuery({ queryKey, queryFn })
    })
  }, [queryClient])

  return {
    clearCache,
    clearSpecificCache,
    getCacheSize,
    prefetchMultipleQueries,
  }
}

// 7. Advanced Error Handling with Retry Logic
function useResilientQuery<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: {
    maxRetries?: number
    retryDelay?: number
    fallbackData?: T
    exponentialBackoff?: boolean
  } = {}
) {
  const {
    maxRetries = 3,
    retryDelay = 1000,
    fallbackData,
    exponentialBackoff = true,
  } = options

  return useQuery({
    queryKey,
    queryFn,
    retry: (failureCount, error: any) => {
      if (failureCount >= maxRetries) return false

      // Don't retry certain error types
      if (error?.status === 401 || error?.status === 403) {
        return false
      }

      return true
    },
    retryDelay: (attemptIndex) => {
      if (exponentialBackoff) {
        return Math.min(1000 * 2 ** attemptIndex, 30000)
      }
      return retryDelay
    },
    onError: (error) => {
      console.error('Query failed:', error)
    },
    fallbackData,
  })
}

// 8. Data Validation and Type Safety
function useValidatedQuery<T, R>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  validator: (data: unknown) => R,
  options: any = {}
) {
  return useQuery({
    queryKey,
    queryFn: async () => {
      try {
        const data = await queryFn()
        return validator(data)
      } catch (error) {
        throw new Error(`Data validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
      }
    },
    ...options,
  })
}

// 9. Memory-efficient Data Management
function usePaginatedData<T>(
  baseQueryKey: string[],
  fetchPage: (page: number) => Promise<{ data: T[]; totalPages: number }>,
  options: {
    pageSize?: number
    cachePages?: number
  } = {}
) {
  const [currentPage, setCurrentPage] = useState(1)
  const { pageSize = 20, cachePages = 3 } = options

  const queryClient = useQueryClient()

  const {
    data,
    isLoading,
    error,
  } = useQuery({
    queryKey: [...baseQueryKey, 'page', currentPage],
    queryFn: () => fetchPage(currentPage),
    keepPreviousData: true,
    staleTime: 1000 * 60 * 5,
  })

  // Prefetch next and previous pages
  useEffect(() => {
    if (data && currentPage < data.totalPages) {
      queryClient.prefetchQuery({
        queryKey: [...baseQueryKey, 'page', currentPage + 1],
        queryFn: () => fetchPage(currentPage + 1),
      })
    }

    if (currentPage > 1) {
      queryClient.prefetchQuery({
        queryKey: [...baseQueryKey, 'page', currentPage - 1],
        queryFn: () => fetchPage(currentPage - 1),
      })
    }
  }, [currentPage, data, baseQueryKey, queryClient])

  return {
    data: data?.data ?? [],
    totalPages: data?.totalPages ?? 0,
    currentPage,
    setCurrentPage,
    isLoading,
    error,
  }
}

// 10. Real-time Data Integration
function useRealtimeSubscription<T>(
  queryKey: string[],
  initialData: T,
  subscribeToUpdates: (callback: (data: T) => void) => () => void
) {
  const queryClient = useQueryClient()

  const { data, isLoading, error } = useQuery({
    queryKey,
    queryFn: () => Promise.resolve(initialData),
    staleTime: Infinity, // Never stale, updated via subscription
  })

  useEffect(() => {
    const unsubscribe = subscribeToUpdates((newData) => {
      queryClient.setQueryData(queryKey, newData)
    })

    return unsubscribe
  }, [queryKey, queryClient, subscribeToUpdates])

  return { data, isLoading, error }
}

// 11. Comprehensive Example: E-commerce Product Management
interface Product {
  id: string
  name: string
  price: number
  description: string
  category: string
  stock: number
  images: string[]
  reviews: Review[]
}

interface Review {
  id: string
  productId: string
  userId: string
  rating: number
  comment: string
  createdAt: string
}

function ProductManagementApp() {
  const queryClient = useQueryClient()
  const [selectedCategory, setSelectedCategory] = useState<string>('all')
  const [searchTerm, setSearchTerm] = useState('')
  const [sortBy, setSortBy] = useState<'name' | 'price' | 'rating'>('name')

  // Fetch products with filters
  const {
    data: products = [],
    isLoading: productsLoading,
    error: productsError,
  } = useQuery({
    queryKey: ['products', { category: selectedCategory, searchTerm, sortBy }],
    queryFn: async () => {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 500))

      let filteredProducts = [
        // Mock data
        { id: '1', name: 'Laptop', price: 999, description: 'High-performance laptop', category: 'electronics', stock: 10, images: [], reviews: [] },
        { id: '2', name: 'Phone', price: 699, description: 'Smartphone', category: 'electronics', stock: 25, images: [], reviews: [] },
        { id: '3', name: 'Book', price: 19, description: 'Good book', category: 'books', stock: 100, images: [], reviews: [] },
      ]

      if (selectedCategory !== 'all') {
        filteredProducts = filteredProducts.filter(p => p.category === selectedCategory)
      }

      if (searchTerm) {
        filteredProducts = filteredProducts.filter(p =>
          p.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
          p.description.toLowerCase().includes(searchTerm.toLowerCase())
        )
      }

      return filteredProducts.sort((a, b) => {
        switch (sortBy) {
          case 'price':
            return a.price - b.price
          case 'name':
            return a.name.localeCompare(b.name)
          default:
            return 0
        }
      })
    },
    staleTime: 1000 * 60 * 5,
  })

  // Create product mutation with optimistic update
  const createProductMutation = useMutation({
    mutationFn: async (newProduct: Omit<Product, 'id' | 'reviews'>) => {
      const response = await fetch('/api/products', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newProduct),
      })
      if (!response.ok) throw new Error('Failed to create product')
      return response.json()
    },
    onMutate: async (newProduct) => {
      await queryClient.cancelQueries({ queryKey: ['products'] })

      const previousProducts = queryClient.getQueryData<Product[]>(['products'])

      const optimisticProduct: Product = {
        ...newProduct,
        id: 'temp-' + Date.now(),
        reviews: [],
      }

      queryClient.setQueryData<Product[]>(['products'], (old = []) => [
        optimisticProduct,
        ...old,
      ])

      return { previousProducts }
    },
    onError: (error, variables, context) => {
      if (context?.previousProducts) {
        queryClient.setQueryData(['products'], context.previousProducts)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] })
    },
  })

  // Update product mutation
  const updateProductMutation = useMutation({
    mutationFn: async ({ id, updates }: { id: string; updates: Partial<Product> }) => {
      const response = await fetch(`/api/products/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(updates),
      })
      if (!response.ok) throw new Error('Failed to update product')
      return response.json()
    },
    onMutate: async ({ id, updates }) => {
      await queryClient.cancelQueries({ queryKey: ['products'] })

      const previousProducts = queryClient.getQueryData<Product[]>(['products'])

      queryClient.setQueryData<Product[]>(['products'], (old = []) =>
        old.map(product =>
          product.id === id ? { ...product, ...updates } : product
        )
      )

      return { previousProducts }
    },
    onError: (error, variables, context) => {
      if (context?.previousProducts) {
        queryClient.setQueryData(['products'], context.previousProducts)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] })
    },
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Product Management</h2>

      {/* Filters and Search */}
      <div className="mb-6 grid grid-cols-1 md:grid-cols-4 gap-4">
        <input
          type="text"
          placeholder="Search products..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          className="px-3 py-2 border rounded"
        />

        <select
          value={selectedCategory}
          onChange={(e) => setSelectedCategory(e.target.value)}
          className="px-3 py-2 border rounded"
        >
          <option value="all">All Categories</option>
          <option value="electronics">Electronics</option>
          <option value="books">Books</option>
        </select>

        <select
          value={sortBy}
          onChange={(e) => setSortBy(e.target.value as any)}
          className="px-3 py-2 border rounded"
        >
          <option value="name">Sort by Name</option>
          <option value="price">Sort by Price</option>
          <option value="rating">Sort by Rating</option>
        </select>

        <button
          onClick={() => createProductMutation.mutate({
            name: 'New Product',
            price: 99,
            description: 'New product description',
            category: 'electronics',
            stock: 50,
            images: [],
          })}
          disabled={createProductMutation.isPending}
          className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
        >
          Add Product
        </button>
      </div>

      {/* Products List */}
      {productsLoading ? (
        <div>Loading products...</div>
      ) : productsError ? (
        <div className="text-red-500">Error loading products</div>
      ) : (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {products.map((product) => (
            <div key={product.id} className="border rounded p-4">
              <h3 className="font-semibold">{product.name}</h3>
              <p className="text-gray-600">{product.description}</p>
              <p className="text-lg font-bold">${product.price}</p>
              <p className="text-sm text-gray-500">Stock: {product.stock}</p>
              <p className="text-sm text-gray-500">Category: {product.category}</p>

              <div className="mt-2 space-x-2">
                <button
                  onClick={() => updateProductMutation.mutate({
                    id: product.id,
                    updates: { price: product.price + 10 }
                  })}
                  disabled={updateProductMutation.isPending}
                  className="px-2 py-1 text-sm bg-blue-500 text-white rounded"
                >
                  Update Price
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

export {
  createAdvancedQueryClient,
  DataProvider,
  useDataContext,
  useSmartFetch,
  useOfflineAwareQuery,
  DataSyncManager,
  useCacheManager,
  useResilientQuery,
  useValidatedQuery,
  usePaginatedData,
  useRealtimeSubscription,
  ProductManagementApp,
}

Tools

Tools frequently paired with this sample

Related

Related sample collections

Tool usage guide

Sample-focused context for search and workflow understanding

Title

TanStack Query Samples

Description

TanStack Query (formerly React Query) examples including data fetching, caching, mutations, optimistic updates, and advanced patterns

Overview

TanStack Query (formerly React Query) examples including data fetching, caching, mutations, optimistic updates, and advanced patterns

When to use

  • Use this sample set when you want reference inputs for TanStack Query Samples.
  • Use it to preview output formats and compare related sample variants.

How it works

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Use cases

  • TanStack Query Basic Usage
  • TanStack Query Advanced Patterns
  • TanStack Query Data Management

FAQ

What is included in TanStack Query Samples?

This page includes curated sample entries, related tools, and neighboring sample collections for TanStack Query Samples.

Home
Tools3197
Samples593
Visualizations461
Viewers103
Hubs229
Tags333
Images, Audio & Video655

Image, audio, and video processing, conversion, and optimization tools

Math, Date & Finance644

Calculators, numerics, date logic, statistics, and finance tools

Design & Color296

Color, layout, graphics, visual styling, and design helper tools

Text & Writing209

Writing, cleanup, formatting, extraction, and text analysis tools

Developer & Web193

Developer utilities, networking, web debugging, and automation helpers

Conversion & Encoding175

Format, file, archive, unit, and encoding conversion tools

UI Screenshot Structure Review and Development Handoff
Piano Sketch Creation, MIDI Review and Listening Demo Handoff
Retro Game Texture Review and Asset Handoff
Screenshot-Based Procedure Guide Review and Handoff
Responsive Page Change Review and Evidence Handoff

© 2026 Elysia Tools. All rights reserved.

DocsPricingAboutTerms of ServicePrivacy PolicyCookiesContact

Development

SQL Query Builder

Build simple SQL queries visually

Network

DNS Query Tool

Query DNS records and domain resolution information including A, AAAA, MX, TXT, NS, and CNAME records

Science & Education

Scientific Constants Query

Search and display scientific constants with customizable language and output format

Development

URL Query Analyzer

Batch parse URL query parameters with output of key frequency, value samples, and encoding anomaly detection

Data Processing

JSONata Query & Transform Studio

Preview JSONata-style queries and transformations, compare results across sample payloads, and export the transformed output as JSON, CSV, YAML, or Markdown

Development

JSONPath Query Tool

Query arbitrary JSON with JSONPath expressions, preview matches, switch output modes, and save reusable query templates

Development

SQL Query Formatter & Minifier

Beautify or minify SQL with full control over dialect (PostgreSQL, MySQL, SQLite, SQL Server/T-SQL, BigQuery, Snowflake, and more), keyword / identifier / function casing, indent style (standard, tabular-left, tabular-right), and spacing between statements. Produces a syntax-highlighted, copy-ready result with live size and statement stats.