Design Converter
Education
Last updated on Mar 26, 2025
•5 mins read
Last updated on Mar 25, 2025
•5 mins read
Fetching data the right way makes React apps smooth and responsive. With libraries like TanStack React Query, handling data has become faster and easier. One useful feature is the useSuspenseQuery hook, which lets components load without extra checks for loading and error states.
This blog explains how to use useSuspenseQuery, manage query data, handle errors, and work with suspense boundaries for better performance.
The useSuspenseQuery hook is part of the popular TanStack React Query library, specifically designed to simplify data fetching using React’s built-in Suspense mechanism. Unlike traditional methods, useSuspenseQuery lets you declaratively manage loading and error handling, significantly reducing boilerplate code.
Here's a quick example of using useSuspenseQuery:
1import { useSuspenseQuery } from '@tanstack/react-query'; 2 3function fetchUser(userId) { 4 return fetch(`/api/users/${userId}`).then(res => res.json()); 5} 6 7function UserProfile({ userId }) { 8 const { data: user } = useSuspenseQuery({ 9 queryKey: ['user', userId], 10 queryFn: () => fetchUser(userId), 11 }); 12 13 return ( 14 <div> 15 <h1>{user.name}</h1> 16 <p>Email: {user.email}</p> 17 </div> 18 ); 19}
In this example, the component won't render until the data returned by the query is ready. Instead, React's suspense fallback handles the loading state gracefully.
Traditionally, you would manually handle loading and error states, often cluttering your components. Using useSuspenseQuery, the hook throws a promise when data is not yet available, causing the suspense component to render the specified fallback component automatically. Likewise, it throws an error if the query fails, triggering the nearest error boundary.
With useSuspenseQuery, background refetches become effortless. By default, the hook updates the UI seamlessly once new data is fetched, without causing unwanted re-renders. React Query smartly manages cache data and automatically updates components that access it.
Here's an example of configuring background fetching with suspense:
1const { data } = useSuspenseQuery({ 2 queryKey: ['posts'], 3 queryFn: fetchPosts, 4 refetchInterval: 60000, // fetch new data every 60 seconds 5});
When your query encounters an error, useSuspenseQuery will automatically propagate the error up to the nearest error boundary. This built-in error handling dramatically simplifies your components and centralizes error management:
1import { ErrorBoundary } from 'react-error-boundary'; 2 3function App() { 4 return ( 5 <ErrorBoundary fallback={<div>Something went wrong!</div>}> 6 <Suspense fallback={<div>Loading...</div>}> 7 <UserProfile userId={42} /> 8 </Suspense> 9 </ErrorBoundary> 10 ); 11}
In the above example, if the UserProfile query fails, React Query throws an error, and your error boundary displays a user-friendly fallback component.
Always ensure the query key is unique and meaningful. React Query uses it for caching query data, so a well-defined key improves efficiency.
1const { data } = useSuspenseQuery({ 2 queryKey: ['products', { page: 1, category: 'electronics' }], 3 queryFn: () => fetchProducts(1, 'electronics'), 4});
You can control error handling behavior using the throwOnError option. If set to false, the hook won't throw errors automatically, and you can manage errors locally.
1const { data, error } = useSuspenseQuery({ 2 queryKey: ['products'], 3 queryFn: fetchProducts, 4 throwOnError: false, 5});
React Query's staleTime controls how long cache data remains fresh. Be mindful of this to avoid unnecessary background refetches causing your UI to re-suspend:
1const { data } = useSuspenseQuery({ 2 queryKey: ['notifications'], 3 queryFn: fetchNotifications, 4 staleTime: 30000, // Data remains fresh for 30 seconds 5});
When you have multiple components needing suspense, consider creating a separate component as a generic loading fallback. This simplifies your component tree and makes maintenance easier.
1function Loader() { 2 return <div>Loading content...</div>; 3} 4 5// Usage: 6<Suspense fallback={<Loader />}> 7 <UserProfile userId={42} /> 8</Suspense>
Using useSuspenseQuery with React Router Dom allows seamless navigation experiences, improving user interaction.
1import { createBrowserRouter, RouterProvider } from 'react-router-dom'; 2 3const router = createBrowserRouter([ 4 { 5 path: '/user/:userId', 6 element: ( 7 <Suspense fallback={<div>Loading user...</div>}> 8 <UserRoute /> 9 </Suspense> 10 ), 11 }, 12]);
For actions triggered by user interaction or non-urgent fetch operations, consider setting options like enabled: false and manually trigger fetches to avoid unnecessary suspense states:
1const { data, refetch } = useSuspenseQuery({ 2 queryKey: ['searchResults', queryString], 3 queryFn: () => fetchSearchResults(queryString), 4 enabled: false, // fetch manually 5}); 6 7const handleSearch = () => refetch();
Encapsulating your queries inside a custom hook enhances reusability and keeps your components clean:
1function useUser(userId) { 2 return useSuspenseQuery({ 3 queryKey: ['user', userId], 4 queryFn: () => fetchUser(userId), 5 }); 6} 7 8// Usage 9function UserProfile({ userId }) { 10 const { data: user } = useUser(userId); 11 12 return <div>{user.name}</div>; 13}
Leveraging useSuspenseQuery effectively can transform your React applications by streamlining data fetching, minimizing boilerplate, and enhancing the overall user experience. By thoughtfully managing your query data, carefully handling loading and error states, and applying suspense optimally, you’ll unlock cleaner, more maintainable React codebases.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.