InitialData vs PlaceholderData in React Query

Just a guy who loves to write code and watch anime.
initialData: "Real" Data That Gets Cached
This is treated as actual data that came from your API. It gets stored in the cache and affects query states:
function useUserProfile(userId) {
return useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
initialData: () => {
// Maybe from another query's cache
// But doesn't have to be
const cachedUser = queryClient.getQueryData(['users'])
?.find(user => user.id === userId)
return cachedUser
}
})
}
// Or static data you know is valid
function useAppConfig() {
return useQuery({
queryKey: ['config'],
queryFn: fetchConfig,
initialData: { theme: 'light', language: 'en' }, // Default config
staleTime: 5 * 60 * 1000
})
}
Key behaviors:
isLoadingwill befalseimmediatelydatais available right awayGets stored in cache with the query key
If stale, will trigger a background refetch
Perfect for seeding cache from other sources
placeholderData: "Fake" Data for UI Only
This is just for show → it doesn't get cached and doesn't affect the query lifecycle:
function useSearchResults(query) {
return useQuery({
queryKey: ['search', query],
queryFn: () => searchAPI(query),
placeholderData: (previousData) => previousData, // Show previous search results
enabled: query.length > 2
})
}
// Or static placeholder
function useProductList(category) {
return useQuery({
queryKey: ['products', category],
queryFn: () => fetchProducts(category),
placeholderData: [
{ id: 1, name: 'Loading...', price: 0 },
{ id: 2, name: 'Loading...', price: 0 },
{ id: 3, name: 'Loading...', price: 0 }
]
})
}
Key behaviors:
isLoadingstaystrueuntil real data arrivesdatashows placeholder butisPlaceholderDataistrueNot stored in cache
Great for smooth transitions and skeleton states
When to Use Each
Use initialData when:
You have actual data from another source (localStorage, other queries, SSR)
You want to populate the cache immediately
The data is "real" and valid to use
// Perfect for SSR or localStorage
function useUserPreferences() {
return useQuery({
queryKey: ['preferences'],
queryFn: fetchPreferences,
initialData: () => {
// From localStorage or server-side rendered data
return JSON.parse(localStorage.getItem('preferences') || '{}')
}
})
}
Use placeholderData when:
You want smooth loading transitions
Showing previous results while fetching new ones
Creating skeleton/loading states
The data is fake and just for UI
// Great for search - keep showing old results while fetching new ones
function useInfiniteSearch(query) {
return useQuery({
queryKey: ['search', query],
queryFn: () => searchAPI(query),
placeholderData: (previousData, previousQuery) => {
// Show previous results while searching
return previousData
}
})
}
Real-World Example: E-commerce Product Page
function ProductPage({ productId }) {
const { data: product, isLoading, isPlaceholderData } = useQuery({
queryKey: ['product', productId],
queryFn: () => fetchProduct(productId),
placeholderData: {
id: productId,
name: 'Loading...',
price: 0,
image: '/placeholder.jpg'
}
})
return (
<div>
<h1 className={isPlaceholderData ? 'opacity-50' : ''}>
{product.name}
</h1>
<img
src={product.image}
alt={product.name}
className={isPlaceholderData ? 'animate-pulse' : ''}
/>
<p>${product.price}</p>
{isLoading && <div>Fetching latest data...</div>}
</div>
)
}
The main difference is: initialData acts like real data, while placeholderData is clearly fake. This impacts caching, loading states, and how React Query manages the data process.






