# Frontend Next State

> Zustand and TanStack React Query patterns for Next.js e-commerce: cart, config, profile, coupon stores, query keys, provider defaults. Use when managing global state, server state, or Zustand stores.

- Skill: `xmuhameed/frontend-next-state` (Agent Skill)
- Install (CLI): `npx skillmds@latest add xmuhameed/frontend-next-state`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xmuhameed/frontend-next-state/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: xmuhameed (https://skillmd.com/u/xmuhameed)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/xmuhameed/frontend-next-state

---


# State Management

## Division

| Concern | Tool |
|---------|------|
| Server/async data | TanStack React Query |
| Cart, config, auth profile | Zustand + persist |
| URL filters | `useSearchParams` |
| Form state | React Hook Form |
| Feature-scoped (checkout draft) | Zustand in `features/checkout/store/` |

## Zustand Stores

| Store | Persist key | Purpose |
|-------|-------------|---------|
| cart | `{brand}-cart` | Line items |
| config | `config-storage` | Country, currency, exchange rates |
| profile | — | Auth user (no persist) |
| favorite | persisted | Wishlist |
| coupon | persisted | Applied promo |
| cart-ui | — | Sheet open state |

## Store Pattern

```typescript
export const useConfigStore = create<ConfigStore>()(
  persist((set, get) => ({
    selectedCountryId: 456,
    exchangeRates: {},
    setSelectedCountry: (id) => set({ selectedCountryId: id }),
  }), {
    name: 'config-storage',
    partialize: (s) => ({ selectedCountryId: s.selectedCountryId }), // exclude isLoading
  })
);
```

Cross-store: `useConfigStore.getState()` inside cart store.

## React Query Defaults

```typescript
staleTime: 2 * 60 * 1000,
gcTime: 5 * 60 * 1000,
refetchOnWindowFocus: false,
retry: 2,
```

## Query Key Factory

```typescript
export const productsKeys = {
  all: ['products'] as const,
  lists: () => [...productsKeys.all, 'list'] as const,
  list: (params) => [...productsKeys.lists(), params] as const,
  detail: (slug) => [...productsKeys.all, 'detail', slug] as const,
};
```

Colocate with service file.

## Invalidation

```typescript
queryClient.invalidateQueries({ queryKey: productsKeys.all });
```

## Auth State Flow

```
ProfileService ↔ useProfileStore ↔ AuthProvider ↔ useAuth()
```

Token in cookie — store holds user object only.

