Manage State like a Pro: Frontend Edition A developer argues that the primary value of frontend engineers has shifted from writing code to evaluating trade-offs, and that state management should be treated as a distribution problem across three distinct domains: server state, URL state, and client state. The post provides a mental model and tooling recommendations for each category, emphasizing the importance of using dedicated server-state tools like TanStack Query and leveraging URL state for shareable views. AI models write code better and faster than devs, but what AI cannot do reliably for your specific product is make the fundamental system design decisions like : "Where should this piece of data actually live, who owns it, and what happens to the user experience when the network fails, the user refreshes, or a link is shared?" The modern frontend engineer’s primary value has shifted from writing code to evaluating trade-offs . If you put every piece of data into a global Redux store or local useState , you aren't just writing messy code, you are making a system design error. To build fast, resilient interfaces, you need to treat state management as a distribution problem across three distinct domains: Server State , Client State , and URL State . You must have come across this type of code before... // Wrong: Blending 3 different types of state into one const products, setProducts = useState ; // Server Data const searchQuery, setSearchQuery = useState "" ; // URL/Navigation Intent const isFilterModalOpen, setIsFilterModalOpen = useState false ; // Local UI useEffect = { // Syncing server state based on local client variables fetchProducts searchQuery .then setProducts ; }, searchQuery ; When you mix these together, your application breaks down in subtle, frustrating ways: To fix this, we need to categorize data by ownership and lifecycle , not just syntax. ┌─────────────────┬───────────────────────────────┬──────────────────────────────────┐ │ State Category │ What It Really Is │ Best Tooling / Pattern │ ├─────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 1. Server State │ External data owned by the DB │ Next.js Cache, TanStack Query, │ │ │ Async, cached, shared │ RTK Query, SWR │ ├─────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 2. URL State │ Navigation intent │ searchParams , React Router, │ │ │ Shareable, persistent │ window.location │ ├─────────────────┼───────────────────────────────┼──────────────────────────────────┤ │ 3. Client State │ Ephemeral UI memory │ useState , useReducer , │ │ │ Local, temporary, interactive │ Zustand, React Context │ └─────────────────┴───────────────────────────────┴──────────────────────────────────┘ The Mental Model: Server state is data that lives on a remote database user profiles, product lists, checkout carts . You do not own this data on the client - you're merely borrowing a temporary snapshot of it. Because server state is asynchronous and shared across multiple users, its main challenges are staleness, caching, deduplication, and revalidation. useState or traditional Redux stores. Use dedicated server-state tools like // Right: Server state managed by a tool built for caching & revalidation const { data: user, isLoading } = useQuery { queryKey: 'user', userId , queryFn: = fetchUser userId , staleTime: 1000 60 5, // 5 minutes before re-fetching } ; The Mental Model: URL state is the input parameter for your entire screen. It represents navigation intent , what specific view, page, or filtered slice of data the user wants to see right now. If a user configures a complex search filter on an e-commerce site, that view should exist as a shareable link. If you trap those filters inside React’s useState , you strip the web of its best native feature: the link. ?search=shoes&page=2 . // URL State as the single source of truth for view parameters export default async function CatalogPage { searchParams } { const { category, page } = await searchParams; // The server/API reads directly from the URL context const products = await getProducts { category, page: Number page || 1 } ; return