DEV Community

Alexey Murz Korepov
Alexey Murz Korepov

Posted on

The Great JavaScript Hook Naming Crisis: An Appeal for Sanity

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
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

Solid.js isn't immune either:

// Solid libraries joining the chaos
import { useQuery } from 'solid-urql';
import { useQuery } from '@tanstack/solid-query';
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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! πŸ•΅οΈβ€β™‚οΈ
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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 πŸ€¦β€β™‚οΈ
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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() { /* ... */ }
Enter fullscreen mode Exit fullscreen mode

But this creates framework-specific problems everywhere.

A Comprehensive Solution: The JavaScript Hook Naming Convention (JHNC)

Core Principles

  1. Prefix with Library Identity
   // ❌ Bad: Generic names
   export { useQuery, useMutation, useClient };

   // βœ… Good: Prefixed names
   export { useApolloQuery, useApolloMutation, useApolloClient };
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode
  1. Maintain Consistency Across Hooks
   // ❌ Bad: Inconsistent naming
   export { 
     useQuery,          // Generic
     apolloClient,      // Prefixed differently
     createMutation     // Different pattern
   };

   // βœ… Good: Consistent pattern
   export { 
     useApolloQuery,
     useApolloClient,
     useApolloMutation
   };
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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

  1. Acknowledge the Problem: Your users are suffering. Listen to them.

  2. Plan the Transition:

    • v3.x: Add prefixed exports alongside generic ones
    • v4.0: Make prefixed exports the default
    • v5.0: Remove generic exports
  3. Update Documentation: Show prefixed imports prominently

  4. Provide Codemods: Help users migrate automatically

   npx @your-lib/codemod add-prefixes
Enter fullscreen mode Exit fullscreen mode

For the Community

  1. Create a Registry: Document which libraries export which generic names

  2. Build Tools:

    • ESLint plugin to detect conflicts
    • VS Code extension for smart aliasing
    • Bundle analyzer to show naming conflicts
  3. 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();
Enter fullscreen mode Exit fullscreen mode

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:

  1. Share this article with library authors
  2. Open issues on libraries you use
  3. Tweet examples of your import alias hell with #JSNamingCrisis
  4. 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)