Every admin panel I've ever built has a users table. Or a products table. Or an orders table. They're all the same shape: rows of records, a couple of status pills, edit and delete actions on the right. And every time, there's a temptation to build the clever version — sorting, filtering, pagination, inline editing — before the panel even works.
In my starter kit, the users table is deliberately dumb. Here's the entire contract:
export default function UsersTable({ users, onEdit, onDelete }) {
Three props. Data in, callbacks out. The component fetches nothing, authenticates nothing, routes nowhere. It renders a table with name, email, a role pill (indigo for admin, gray for everyone else), a formatted creation date, and Edit/Delete buttons that call the callbacks with the user object.
That's it. And that restraint is the feature.
Because the component knows nothing about where the data comes from, the parent page owns all the interesting decisions: how users are fetched, what the edit modal looks like, what happens after a delete (refetch? optimistic removal? confirmation dialog?). I can reuse this exact component in a demo build that runs against an in-memory mock API — the kit has a demoMock seam for exactly that — and in production against the real Laravel API, without changing a line of it. Props in, actions out. The component doesn't care.
This is also how I keep admin features cheap to add later. When a client asks for a products table next month, I don't build a new abstraction; I copy this file, change the columns, and wire new callbacks. Ninety percent of the code is identical. People call this boring. Boring is what lets me ship an admin panel in an afternoon.
A few small details I do keep in the "dumb" version: overflow-x-auto on the wrapper so the table scrolls horizontally on mobile instead of breaking the layout; whitespace-nowrap on cells so dates don't wrap weirdly; hover:bg-gray-50 on rows because affordance matters even in internal tools. And the role pill — a tiny inline conditional — because the person staring at this table is an admin, and the fastest thing their eyes need is "who here is an admin."
The lesson I keep re-learning: the smartest component in your admin panel is the one that knows the least. Push the knowledge up to the page, keep the table stupid, and you'll thank yourself on every project after this one.
The full admin loop — this table, the Users page that drives it, the API behind it, and the tests proving it — ships in my Laravel + React SaaS Starter Kit, $19 one-time. The admin panel is done; go build the product.
Top comments (0)