We Need to Talk About useQuery, useClient, and the Epidemic of Generic Names
If you're a JavaScript developer in 2026, you've probably experienced this scenario: You're building an app that needs to fetch data from multiple sourcesβGraphQL API, REST endpoints, a search service, maybe some real-time subscriptions. You type useQ... and your IDE has a complete meltdown:
useQuery - from '@apollo/client' // GraphQL queries
useQuery - from 'urql' // Also GraphQL
useQuery - from '@tanstack/react-query' // REST/HTTP queries
useQuery - from 'react-relay' // GraphQL with Relay
useQuery - from 'graphql-hooks' // More GraphQL
useQuery - from '@supabase/react' // Database queries
useQuery - from 'react-firebase-hooks' // Firestore queries
useQuery - from '@algolia/react-hooks' // Search queries
useQuery - from 'swr' // HTTP data fetching
useQuery - from... dear god, make it stop
Welcome to the Great JavaScript Hook Naming Crisis β a developer experience catastrophe that spans React, Vue, Solid, and every modern framework.
The Problem Is Everywhere and It's Multiplying
Every Type of Query, Same Name
The word "query" has become meaningless in modern JavaScript. Here's the same function name doing completely different things:
// GraphQL query
const { data } = useQuery(gql`
query GetUser($id: ID!) {
user(id: $id) { name, email }
}
`);
// REST API query
const { data } = useQuery(['user', id],
() => fetch(`/api/users/${id}`)
);
// Database query
const { data } = useQuery(
supabase.from('users').select('*').eq('id', id)
);
// Search query
const { data } = useQuery('users', {
query: 'John',
filters: 'role:admin'
});
// Firestore query
const { data } = useQuery(
collection(firestore, 'users'),
where('active', '==', true)
);
Same hook name, completely different APIs, parameters, and behaviors. This isn't just confusingβit's a recipe for bugs.
The Import Statement Hall of Shame
Here's a real imports section from a production app:
// π€― Actual imports from a real project
import { useQuery as useGraphQLQuery } from '@apollo/client';
import { useQuery as useRESTQuery } from '@tanstack/react-query';
import { useQuery as useFirestoreQuery } from 'react-firebase-hooks/firestore';
import { useQuery as useSupabaseQuery } from '@supabase/react';
import { useMutation as useGraphQLMutation } from 'urql';
import { useMutation as useRESTMutation } from '@tanstack/react-query';
import { useClient as useGraphQLClient } from '@apollo/client';
import { useClient as useSupabaseClient } from '@supabase/auth-helpers-react';
import { useAuth as useFirebaseAuth } from 'react-firebase-hooks/auth';
import { useAuth as useClerkAuth } from '@clerk/nextjs';
import { useAuth as useAuth0 } from '@auth0/nextjs-auth0';
This is not an exaggeration. This is reality.
It's Not Just React Anymore
Vue 3's Composition API faces the same crisis:
// Vue libraries with conflicting names
import { useQuery } from '@vue/apollo-composable';
import { useQuery } from '@tanstack/vue-query';
import { useQuery } from 'villus';
import { useQuery } from '@urql/vue';
Solid.js isn't immune either:
// Solid libraries joining the chaos
import { useQuery } from 'solid-urql';
import { useQuery } from '@tanstack/solid-query';
Beyond Queries: The Full Catastrophe
The problem extends far beyond useQuery:
// Authentication hooks
useAuth - from '@clerk/nextjs'
useAuth - from '@auth0/nextjs-auth0'
useAuth - from 'react-firebase-hooks'
useAuth - from '@supabase/auth-helpers'
useAuth - from 'next-auth/react'
// Client/connection hooks
useClient - from 'urql'
useClient - from '@apollo/client'
useClient - from '@supabase/react'
useClient - from 'graphql-hooks'
// Store/state hooks
useStore - from 'zustand'
useStore - from 'valtio'
useStore - from 'jotai'
useStore - from 'nanostores'
// Subscription hooks
useSubscription - from '@apollo/client'
useSubscription - from 'urql'
useSubscription - from '@supabase/react'
useSubscription - from 'react-relay'
The Hidden Costs Are Massive
1. The Wrong Import Bug
// Developer meant to use REST query but auto-import grabbed GraphQL
import { useQuery } from '@apollo/client'; // π±
function UserList() {
// This will crash at runtime with cryptic errors
const { data } = useQuery('/api/users', fetchUsers);
}
2. The Refactoring Nightmare
Switching from one library to another? Hope you enjoy manually checking every single import:
// Before: Using Apollo
import { useQuery } from '@apollo/client';
// After: Switching to urql
import { useQuery } from 'urql';
// But wait! You also have React Query in the same file
// Which useQuery was which? Time to audit everything! π΅οΈββοΈ
3. The Onboarding Disaster
New developer: "Why does useQuery work differently in each file?"
Senior dev: "Oh, that's because... opens spreadsheet ...in this file it's Apollo, in that file it's React Query, in the other file it's Supabase, and in the component you're looking at, it's actually SWR but imported as useQuery through an alias we created two years ago and forgot about."
New developer: quietly updates LinkedIn
Real-World Horror Stories
The Multi-Database Disaster
// A real component that needs data from multiple sources
function Dashboard() {
// GraphQL for user data
const { data: userData } = useQuery(GET_USER);
// REST for analytics
const { data: analytics } = useQuery(['analytics'], fetchAnalytics);
// Firestore for real-time updates
const [messages] = useQuery(messagesQuery);
// Which useQuery is which? π€·ββοΈ
// Spoiler: They imported the wrong one for analytics
}
The Case of the Mysterious Type Error
// TypeScript tries to help but makes it worse
const { data } = useQuery(/* ... */);
// Type error: Argument of type 'DocumentNode' is not assignable to parameter of type 'QueryKey'
// Translation: You imported React Query instead of Apollo π€¦ββοΈ
How Did We Get Here?
The "Beautiful API" Delusion
Every library author thinks their API is special:
// "Our useQuery is the most elegant!" - Every library author
const { data } = useQuery(key, fetcher, options);
But when everyone has the same "beautiful" API, the ecosystem becomes ugly.
The Standards That Aren't
JavaScript has specs for everything except what actually matters to developers:
- ECMAScript: β Specifies semicolon rules
- TC39: β Debates optional chaining syntax
- Anyone: β "Hey, maybe don't all use the same hook names?"
The Framework Agnostic Trap
Libraries pride themselves on being "framework agnostic":
// "Works with any framework!"
export function useQuery() { /* ... */ }
But this creates framework-specific problems everywhere.
A Comprehensive Solution: The JavaScript Hook Naming Convention (JHNC)
Core Principles
- Prefix with Library Identity
// β Bad: Generic names
export { useQuery, useMutation, useClient };
// β
Good: Prefixed names
export { useApolloQuery, useApolloMutation, useApolloClient };
- Be Specific About Query Types
// β Bad: What kind of query?
export { useQuery };
// β
Good: Clear query type
export { useGraphQLQuery }; // Apollo, urql
export { useHTTPQuery }; // React Query, SWR
export { useDBQuery }; // Supabase, Firebase
export { useSearchQuery }; // Algolia, ElasticSearch
- Maintain Consistency Across Hooks
// β Bad: Inconsistent naming
export {
useQuery, // Generic
apolloClient, // Prefixed differently
createMutation // Different pattern
};
// β
Good: Consistent pattern
export {
useApolloQuery,
useApolloClient,
useApolloMutation
};
Migration Strategy
Libraries should provide both during transition:
// New prefixed exports (default)
export {
useTanStackQuery,
useTanStackMutation,
useTanStackQueryClient
} from './hooks';
// Legacy exports (deprecated)
export {
useTanStackQuery as useQuery,
useTanStackMutation as useMutation,
useTanStackQueryClient as useQueryClient
} from './hooks/legacy';
Naming Guidelines by Category
GraphQL Libraries:
-
useApolloQuery,useUrqlQuery,useRelayQuery
HTTP/REST Libraries:
-
useTanStackQuery,useSWRQuery,useAxiosQuery
Database Libraries:
-
useSupabaseQuery,useFirestoreQuery,usePrismaQuery
Search Libraries:
-
useAlgoliaSearch,useElasticQuery,useMeilisearch
Authentication Libraries:
-
useClerkAuth,useAuth0Auth,useSupabaseAuth
The Path Forward
For Library Authors
Acknowledge the Problem: Your users are suffering. Listen to them.
-
Plan the Transition:
- v3.x: Add prefixed exports alongside generic ones
- v4.0: Make prefixed exports the default
- v5.0: Remove generic exports
Update Documentation: Show prefixed imports prominently
Provide Codemods: Help users migrate automatically
npx @your-lib/codemod add-prefixes
For the Community
Create a Registry: Document which libraries export which generic names
-
Build Tools:
- ESLint plugin to detect conflicts
- VS Code extension for smart aliasing
- Bundle analyzer to show naming conflicts
Establish Standards: Push for JHNC adoption in popular libraries
For Framework Authors
Consider namespace imports as a first-class pattern:
// Future: Framework-level namespacing?
import * as Apollo from '@apollo/client/hooks';
import * as TanStack from '@tanstack/react-query/hooks';
Apollo.useQuery();
TanStack.useQuery();
The Cost of Inaction
Every day we don't fix this:
- Thousands of developers waste time on import conflicts
- Hundreds of bugs ship due to wrong auto-imports
- Dozens of new developers get frustrated and confused
- Every new library makes the problem worse
Join the Movement
This isn't about personal preference. It's about collective productivity. The JavaScript ecosystem is mature enough to solve this.
What you can do today:
- Share this article with library authors
- Open issues on libraries you use
- Tweet examples of your import alias hell with #JSNamingCrisis
- Vote with your choice: Support libraries that respect developer experience
For library authors reading this: Your move. Will you be part of the problem or part of the solution?
Let's make 2026 the year we finally fixed JavaScript's naming crisis.
Not because it's elegant. Not because it's beautiful. But because thousands of developers are begging for it.
The time for "not my problem" is over. This is everyone's problem. And it's time we solved it together.
Join the discussion: #JSNamingCrisis #JHNC
Have your own horror story? Share it. Let's document the pain and push for change.
P.S. The appeal was actually suggested to me by AI when I asked it why there were so many conflicting names in JS libraries, so blame it on the AI, not me. π
Top comments (0)