INTRO
Dashboards are never “finished.” New columns, new filters, and new exports arrive weekly. If data fetching, table config, and layout share one file, every change risks regressions. I separate **what** the screen loads from **how** it renders.
Split data loading from presentation
A page component should orchestrate: read params, trigger queries, handle errors, pass props. Presentational components receive rows, loading flags, and callbacks - not raw axios instances.
With TanStack Query (or similar), I colocate query keys and fetchers per feature folder. Screens import hooks like `useOrdersList` instead of inlining `useEffect` + `fetch`.
code-snippet.tsx
// features/orders/OrdersPage.tsx
export function OrdersPage() {
const { data, isPending, error } = useOrdersList()
if (error) return <QueryErrorBanner error={error} />
return <OrdersTable rows={data ?? []} loading={isPending} />
}Repeatable table and form patterns
I standardize column definitions (header, accessor, sort key) in one shape so exporting CSV and rendering `<th>` stay in sync. One-off `<td>` spaghetti breaks sorting and exports first.
Forms share field components with consistent validation messages. Zod schemas on the client can mirror server validation to reduce round-trips.
Routing and URL state for shareable views
Filters belong in the URL when users need to share a view: `?status=open&assignee=me`. It also makes back/forward predictable.
Deep-link modals sparingly; they complicate SSR and testing. Prefer dedicated routes for complex edit flows when possible.
Performance includes comprehension speed
Virtualized lists help huge tables, but before that I question whether the API should paginate or project fewer fields. Sending 5MB of JSON to paint 50 rows is a backend contract issue.
Memoization is a last step. Structure and fewer renders beat `useMemo` everywhere.
KEY TAKEAWAYS
“Keep fetch logic in hooks/services; keep UI components thin.”
- 02
Unify table definitions so UI, sort, and export do not diverge.
- 03
Put shareable filters in the query string.
- 04
Right-size API payloads before micro-optimizing React.
CONCLUSION
Closing thought
Maintainable dashboards are boring on purpose: predictable folders, explicit data boundaries, and URLs that reflect state. That boredom is what lets you ship the next feature without rewriting the last one. Need an ops console estimate? See the timeline article, then Contact.
CONTINUE
Related pages
UP NEXT
HTTP Caching vs Redis Application Cache
Splitting browser/CDN cache headers from server-side Redis cache for hot reads. When `Cache-Control` is enough and when you need explicit invalidation.
Read next article