"State" is the data your interface depends on at any moment: who is signed in, what is in the cart, whether a menu is open, the list of orders fetched from your API. As an application grows, deciding where each piece of state lives, and which library manages it, has a big effect on how easy the code is to change. This guide explains the kinds of state, the main libraries in 2026, and how to choose between them without over-engineering.
Split state into three kinds: local UI state (keep it in the component), shared client state (a small store such as Zustand or Redux Toolkit, or the framework's own tools), and server state (data from an API, best handled by a data-fetching library such as TanStack Query). Most apps need far less global state than they think once server data is handled separately. Start with the framework's built-in tools and add a library only when prop-passing or duplicated fetching becomes painful.
1. The three kinds of state
| Kind | Examples | Where it belongs |
|---|---|---|
| Local UI state | Form inputs, an open modal, the active tab | Inside the component (useState, a Vue ref, a Svelte $state) |
| Shared client state | Signed-in user, theme, cart, feature flags | A small global store or context |
| Server state | Products, orders, anything fetched from an API | A data-fetching and caching library |
Server state is different from the other two. It is owned by the server, it can go stale, several screens may need the same data, and it has loading and error states. Treating it like ordinary global state, by copying API responses into a Redux store by hand, is the most common reason state management feels complicated. A data-fetching library handles caching, refetching and deduplication for you.
A fourth kind is often forgotten: URL state. Filters, search terms, page numbers and the selected item usually belong in the URL, so that a page can be bookmarked, shared and restored with the back button.
2. Principles that apply to every library
- One source of truth. Each piece of data has one owner. Derive everything else (totals, filtered lists) from it instead of storing copies that can disagree.
- Keep state as local as possible. Lift it up only when a second component truly needs it.
- Update immutably. Replace objects rather than changing them in place, so the framework can see what changed. Libraries such as Redux Toolkit and Zustand with Immer let you write "mutating" code that is turned into immutable updates.
- Predictable updates. Change shared state through named actions or functions, not from anywhere, so you can trace why it changed.
- Subscribe narrowly. Components should read only the slice of state they use, so an unrelated change does not re-render them.
3. React: built-in tools first
React's own tools cover more than people expect:
useStateanduseReducerfor local and moderately complex component state;- Context for values many components read but that change rarely, such as the theme, the signed-in user or the locale.
Context re-renders every consumer when its value changes, so it is a poor fit for fast-changing data such as a cart being edited or a live form. That is where a store earns its place.
4. Shared client state libraries
Redux Toolkit
Redux Toolkit (RTK) is the official, current way to write Redux. The old hand-written pattern of action constants, switch statements and createStore is outdated; do not start new code that way. RTK suits large teams that want strict conventions, excellent DevTools with time-travel debugging, and middleware.
import { createSlice, configureStore } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [] },
reducers: {
addItem: (state, action) => { state.items.push(action.payload); },
clear: (state) => { state.items = []; },
},
});
export const { addItem, clear } = cartSlice.actions;
export const store = configureStore({ reducer: { cart: cartSlice.reducer } });In components, useSelector reads a slice and useDispatch sends actions. RTK also includes RTK Query for server state, which is a good choice if you are already on Redux.
Zustand
Zustand is a small store with very little boilerplate. It is a common default for React apps that need some shared state but not Redux's ceremony.
import { create } from 'zustand';
export const useCart = create((set) => ({
items: [],
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
clear: () => set({ items: [] }),
}));
// In a component: read only what you need
const count = useCart((s) => s.items.length);Jotai and MobX
Jotai stores state as small independent "atoms" that components combine, which suits apps with many small, interdependent values. MobX uses observable objects: you change data directly and the components that read it update automatically. It is productive for complex, object-heavy domains, but the automatic tracking can make data flow less obvious in a large team.
Meta archived the Recoil repository in 2025. Don't choose it for new projects; Jotai offers a similar atom-based model and is actively maintained.
5. Server state: TanStack Query and friends
TanStack Query (formerly React Query, also available for Vue, Svelte, Solid and Angular) fetches, caches and refreshes server data. You describe what to fetch, and it handles loading and error states, caching, background refetching and retries.
import { useQuery } from '@tanstack/react-query';
function Orders() {
const { data, isPending, error } = useQuery({
queryKey: ['orders'],
queryFn: () => fetch('/api/orders').then((r) => r.json()),
});
if (isPending) return <p>Loading...</p>;
if (error) return <p>Could not load orders.</p>;
return <ul>{data.map((o) => <li key={o.id}>{o.number}</li>)}</ul>;
}Alternatives: SWR is a lighter React option; RTK Query fits Redux apps; Apollo Client and urql are the usual choices for GraphQL APIs. Frameworks with server rendering, such as Next.js, also let you fetch data on the server, which removes some client-side server state altogether.
6. Other frameworks
| Framework | Built-in tools | Common store |
|---|---|---|
| Vue 3 | ref, reactive, computed, provide/inject | Pinia (the official store; Vuex is legacy) |
| Angular | Signals and services | NgRx (including the lighter NgRx SignalStore) |
| Svelte 5 | Runes such as $state and $derived | Shared state in .svelte.js modules; stores for older code |
| React Native | Same as React | Zustand, Redux Toolkit, TanStack Query |
| Flutter | setState, InheritedWidget | Riverpod, Bloc, Provider |
For mobile and offline-first apps, you also need to persist state across restarts (for example with Zustand's persist middleware or TanStack Query's persister) and to queue changes made offline until the connection returns.
7. How to choose
Test state logic where it lives: reducers and store actions are plain functions, so they can be unit-tested without rendering. For components, test behaviour with Testing Library and mock the network with a tool such as Mock Service Worker rather than mocking the store. For performance, subscribe to narrow slices, memoise expensive derived values, and virtualise long lists instead of trimming state.
8. Running your app on Domain India
State management runs in the browser, so where you host depends on how your app is built:
- A built single-page app (React, Vue or Svelte compiled to static files) can be uploaded to any shared hosting plan. Upload the build output to
public_htmland add an.htaccessrewrite that sends unknown paths toindex.html, so client-side routes survive a page refresh; mod_rewrite is loaded on our cPanel, DirectAdmin and Webuzo servers. See How can I enable the mod_rewrite module. - Build on your own computer or in CI, not on the server, then upload the result.
- A Node.js back end or server-rendered app can run with cPanel's Setup Node.js App tool (Node.js 20, 22 and 24 are enabled on our cPanel servers) or on the App Platform, where Node.js apps are detected automatically and other stacks run from a Dockerfile. See Deploy a Node.js app on shared hosting and Getting started with the App Platform.
- 512 MB RAM per app
- 1 vCPU
- 5 GB NVMe SSD
- PostgreSQL Database
The price on the card is a live Domain India list price and excludes 18% GST. For a full example that connects a React front end to an Express API, see Building a weather app with MySQL, Express, React and Node.js.
Frequently asked questions
Do I need Redux in a React app?
Usually not at the start. React's useState, useReducer and Context, plus a data-fetching library such as TanStack Query for API data, cover most apps. Add Redux Toolkit or Zustand when several distant components share client state that changes often.
What is the difference between client state and server state?
Client state is owned by the browser, such as a theme or an open modal. Server state is owned by your back end, such as orders or products fetched from an API. Server state can go stale and needs caching and refetching, which is why a library like TanStack Query handles it better than a general store.
Is Redux still relevant in 2026?
Yes, as Redux Toolkit, the official modern way to write Redux. It suits large teams that want strict conventions and strong DevTools. The old hand-written Redux pattern with createStore and switch statements is outdated.
Should I use Vuex or Pinia for Vue 3?
Pinia. It is the official state management library for Vue 3, and Vuex is in maintenance mode.
Is Recoil still a good choice?
No. Meta archived the Recoil repository in 2025. Jotai offers a similar atom-based approach and is actively maintained.
Can I host a React or Vue single-page app on shared hosting?
Yes. Build the app on your computer or in CI, upload the static files to public_html, and add an .htaccess rule that sends unknown paths to index.html so client-side routing works after a refresh.
Ready to deploy your app? Upload a built front end to cPanel hosting, or run a Node.js or Docker-based app on the App Platform. If you are not sure which fits your project, open a support ticket and describe your stack.
Node.js apps are detected automatically, and anything else runs from a Dockerfile. Plans start with the Starter tier.
See App Platform plans