{"slug": "manage-state-like-a-pro-frontend-edition", "title": "Manage State like a Pro: Frontend Edition", "summary": "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.", "body_md": "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*:\n\n\"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?\"\n\nThe modern frontend engineer’s primary value has shifted from *writing code* to *evaluating trade-offs*.\n\nIf you put every piece of data into a global Redux store or local `useState`\n\n, 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**.\n\nYou must have come across this type of code before...\n\n```\n// Wrong: Blending 3 different types of state into one\n\nconst [products, setProducts] = useState([]); // Server Data\nconst [searchQuery, setSearchQuery] = useState(\"\"); // URL/Navigation Intent\nconst [isFilterModalOpen, setIsFilterModalOpen] = useState(false); // Local UI\n\nuseEffect(() => {\n  // Syncing server state based on local client variables\n  fetchProducts(searchQuery).then(setProducts);\n}, [searchQuery]);\n```\n\nWhen you mix these together, your application breaks down in subtle, frustrating ways:\n\nTo fix this, we need to categorize data by **ownership and lifecycle**, not just syntax.\n\n```\n┌─────────────────┬───────────────────────────────┬──────────────────────────────────┐\n│ State Category  │ What It Really Is             │ Best Tooling / Pattern           │\n├─────────────────┼───────────────────────────────┼──────────────────────────────────┤\n│ 1. Server State │ External data owned by the DB │ Next.js Cache, TanStack Query,   │\n│                 │ (Async, cached, shared)       │ RTK Query, SWR                   │\n├─────────────────┼───────────────────────────────┼──────────────────────────────────┤\n│ 2. URL State    │ Navigation intent             │ `searchParams`, React Router,    │\n│                 │ (Shareable, persistent)       │ `window.location`                │\n├─────────────────┼───────────────────────────────┼──────────────────────────────────┤\n│ 3. Client State │ Ephemeral UI memory           │ `useState`, `useReducer`,        │\n│                 │ (Local, temporary, interactive)│ Zustand, React Context            │\n└─────────────────┴───────────────────────────────┴──────────────────────────────────┘\n```\n\n*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.\n\nBecause server state is asynchronous and shared across multiple users, its main challenges are **staleness, caching, deduplication, and revalidation.**\n\n`useState`\n\nor traditional Redux stores. Use dedicated server-state tools like \n\n```\n// Right: Server state managed by a tool built for caching & revalidation\nconst { data: user, isLoading } = useQuery({\n  queryKey: ['user', userId],\n  queryFn: () => fetchUser(userId),\n  staleTime: 1000 * 60 * 5, // 5 minutes before re-fetching\n});\n```\n\n**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.\n\nIf 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`\n\n, you strip the web of its best native feature: **the link.**\n\n`?search=shoes&page=2`\n\n).\n\n```\n// URL State as the single source of truth for view parameters\nexport default async function CatalogPage({ searchParams }) {\n  const { category, page } = await searchParams;\n\n  // The server/API reads directly from the URL context\n  const products = await getProducts({ category, page: Number(page) || 1 });\n\n  return <ProductGrid products=\"{products}\"/>;\n}\n```\n\n**The Mental Model:** Client state is purely local, temporary UI memory. It does not come from a database, and it doesn't need to survive a page share or a browser refresh.\n\nIf the user closes the tab, this data can safely disappear without ruining their experience.\n\n`useState`\n\nor `useReducer`\n\n. If it needs to be accessed globally across unrelated components (like a sidebar collapse state), use a lightweight client store like \n\n```\n// Simple, isolated local client state\nconst [isSidebarOpen, setIsSidebarOpen] = useState(false);\n```\n\nWhen designing a feature, ask yourself these three sequential questions to determine where state belongs:\n\n```\n                     [New Piece of State]\n                              │\n              Does it come from an API/Database?\n             ┌────────────────┴────────────────┐\n            YES                                NO\n             │                                 │\n     (1. SERVER STATE)              Should this view be shareable \n  Use: Next.js / TanStack           or survive a page refresh?\n                                   ┌───────────┴───────────┐\n                                  YES                      NO\n                                   │                       │\n                            (2. URL STATE)          (3. CLIENT STATE)\n                          Use: searchParams        Use: useState / Zustand\n```\n\n`/products?category=shoes&q=nike`\n\n). If the user emails this link to a friend, the friend sees the exact same Nike shoes.Writing syntax is rapidly becoming an automated commodity. You can prompt an AI tool to write a complex reducer or a fetch handler in seconds.\n\nHowever, choosing **where state lives**, **how it expires**, and **how it travels across the network** requires engineering judgment.\n\nBy cleanly separating your architecture into **Server State** (remote truth), **URL State** (navigation truth), and **Client State** (interactive truth), you build applications that are inherently faster, far more resilient, and built to scale.", "url": "https://wpnews.pro/news/manage-state-like-a-pro-frontend-edition", "canonical_source": "https://dev.to/peterintech/manage-state-like-a-pro-frontend-edition-1id0", "published_at": "2026-08-20 16:32:06+00:00", "updated_at": "2026-08-20 16:44:47.742446+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Redux", "TanStack Query", "RTK Query", "SWR", "React Router", "Zustand", "React Context", "Next.js"], "alternates": {"html": "https://wpnews.pro/news/manage-state-like-a-pro-frontend-edition", "markdown": "https://wpnews.pro/news/manage-state-like-a-pro-frontend-edition.md", "text": "https://wpnews.pro/news/manage-state-like-a-pro-frontend-edition.txt", "jsonld": "https://wpnews.pro/news/manage-state-like-a-pro-frontend-edition.jsonld"}}