# E-commerce Dashboard

> A full-featured admin panel using TanStack Query for server state and TanStack Table for data rendering with real-time updates.

**Principle:** Dashboard architecture separates data fetching from UI rendering. TanStack Query manages all server state — metrics, orders, inventory — while TanStack Table handles display logic. This eliminates the need for manual caching and pagination state.

**Tip:** Centralize all query keys in a factory file. This prevents key collisions and makes invalidation predictable across your entire dashboard.

**Rules:**
- **Query Key Factory** — Use hierarchical array keys: ["orders", "list", filters] for precise cache control.
- **Separate Data Layers** — Keep fetch functions outside components. Services folder → custom hooks → UI.
- **Prefetch on Hover** — Prefetch linked records on hover to make navigation feel instant.
- **Optimistic UI** — Apply mutations optimistically and roll back on error for a snappy user experience.

**Pattern** — `hooks/queries/useDashboardMetrics.ts`

```ts
import { useQuery } from '@tanstack/react-query';
import { dashboardKeys } from '@/lib/query-keys';

export const useDashboardMetrics = (range: DateRange) => {
  return useQuery({
    queryKey: dashboardKeys.metrics(range),
    queryFn: () => fetchMetrics(range),
    staleTime: 1000 * 60 * 5, // 5 minutes
    placeholderData: (prev) => prev,
  });
};
```

**Implementation — Next.js**

In Next.js App Router, prefetch dashboard data in the server component and pass it to the client via HydrationBoundary for instant initial render.

File: `app/dashboard/page.tsx`

```tsx
import { HydrationBoundary, dehydrate } from '@tanstack/react-query';
import { getQueryClient } from '@/lib/get-query-client';

export default async function DashboardPage() {
  const qc = getQueryClient();
  await qc.prefetchQuery({
    queryKey: dashboardKeys.metrics('7d'),
    queryFn: () => fetchMetrics('7d'),
  });
  return (
    <HydrationBoundary state={dehydrate(qc)}>
      <DashboardClient />
    </HydrationBoundary>
  );
}
```

**Implementation — Vite**

In Vite, wrap your app in QueryClientProvider and use the custom hook directly in client components. Queries run on mount.

File: `pages/DashboardPage.tsx`

```tsx
import { useDashboardMetrics } from '@/hooks/queries/useDashboardMetrics';

export function DashboardPage() {
  const { data, isLoading, isError } = useDashboardMetrics('7d');

  if (isLoading) return <MetricsSkeleton />;
  if (isError) return <ErrorAlert />;

  return <MetricsGrid data={data} />;
}
```

Read more: https://reactprinciples.dev/cookbook/e-commerce-dashboard

---

Source: https://reactprinciples.dev/nextjs/cookbook/e-commerce-dashboard
All recipes (compact): https://reactprinciples.dev/llms.txt
All recipes (full): https://reactprinciples.dev/llms-full.txt
