Kinds of state and where they live
Not all state is the same: local UI state, shared client state, server state, URL state and form state each have a best home. Colocate state, lift it only when needed, and keep filters in the URL.
Comes up in: React, Frontend system design
Learn#
Rahul’s dashboard keeps everything in one Redux store: API data, an open dropdown, filters and form drafts. Refreshing the page loses the filters, the API data goes stale, and every keystroke updates the global store.
State comes in different kinds, and each has a natural home:
- Local UI state, such as an open dropdown:
useStatein that component. - Server state: a cached copy of data the server owns. It needs fetching, caching and refetching, best handled by a library such as TanStack Query (RCT.09.02).
- Client state shared by many components, such as a theme or a cart: context or a small store.
- URL state: filters, tabs, pages and search terms that should survive reload and sharing.
- Form state: drafts and validation, owned by the form (RCT.10.01).
URL state takes no library:
// Filter state that lives in the URL survives reloads, back/forward and shared links.
const url = new URL("https://shop.example/products?category=shoes&size=8&size=9&sort=price");
const params = url.searchParams;
const filters = { category: params.get("category"), sizes: params.getAll("size"), sort: params.get("sort") ?? "popular" };
console.log(filters);
params.set("sort", "newest");
params.delete("size", "9");
console.log(url.search);Output (verified)
{ category: 'shoes', sizes: [ '8', '9' ], sort: 'price' }
?category=shoes&size=8&sort=newestState
- store: orders, isDropdownOpen, filters, draft
The dashboard keeps everything in one global store: orders from the API, the open dropdown, the filters, the form draft.
Read the steps as text
- The dashboard keeps everything in one global store: orders from the API, the open dropdown, the filters, the form draft.
- Orders are server state: a cached copy of data the server owns. They go stale and need refetching.
- isDropdownOpen matters to one component: local state. filters should survive reload and sharing: URL state.
- The draft belongs to the form; only the theme and the cart remain as shared client state.
Predict#
Predict first
A user filters products by size and price, shares the link with a friend, and the friend sees all products. Where should the filters have lived?
Anything a user expects to bookmark, share or restore with Back belongs in the URL. Component state and localStorage stay on one device and one session.
Interview angle#
Say it like this — about 30 seconds
“I separate state by owner. Server state is a cached copy of server data, with staleness and refetching, so I use a server-state library. Client state is owned by the browser: local UI state stays in the component, and shared things like theme or cart go in context or a small store. Filters, tabs and pagination go in the URL so they survive reloads and can be shared. Forms own their drafts. I colocate state as low as possible and lift it only when two components need it.”
Follow-up What is the difference between server state and client state?
Follow-up Should filter state live in the URL? Why?
Follow-up What does colocation mean?
Practice#
Guided
Store product-list filters in the URL: serialize without defaults, and parse back with types.
Done when
- Default values are left out, and an all-default state gives "".
- Sizes become repeated
sizeparams, in order. fromSearchrestores defaults and makespagea number.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
What is server state?
A cached client copy of data the server owns, which can go stale.
Where should filters and pagination live?
In the URL.
What is colocation?
Keeping state in the lowest component that needs it.
Which API reads all values of a repeated query parameter?
URLSearchParams.getAll(name).
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Check yourself: When should you lift state up?
When two or more components need the same value.
How it works underneathSenior rounds
Update the URL with history.replaceState (or the router) while the user types, and push a new entry for deliberate choices such as switching category, so Back behaves as users expect (WEB.18.01).
Check yourself: push or replace when updating filters in the URL?
Usually replace for typing, push for deliberate changes users may want to go Back to.
Edge cases and trapsWhere people slip
URLs are user input: values can be missing, misspelled or malicious. Parse with defaults and validate (TS.08.04). Keep URLs readable and stable, because users bookmark them.
Check yourself: Why validate URL state?
Users can edit URLs, so values may be missing or invalid.
In productionStaff-level answers
Libraries such as nuqs or TanStack Router keep URL state typed and in sync with components Source: TanStack Router: Search params. Server state belongs in a cache library, not a global store.
Check yourself: Name a library that types and validates search params.
For example nuqs, or TanStack Router's search params.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- What is the difference between server state and client state?
- Should filter state live in the URL? Why?