Skip to main content
zerotal

Admin Panel

@zerotal/admin is a Filament-style admin panel for Zerotal. You describe each model with a Resource class — its table columns, form fields, infolist entries, filters, actions, and relations — and the panel renders fully reactive CRUD pages.

It is server-driven: pages are @zerotal/flow components and the UI is built from @zerotal/flow-ui, so sorting, filtering, inline edits, modals, and live notifications all round-trip over Flow's WebSocket morph with no client store to maintain. Light + dark mode ship out of the box.

Note — This is the current, class-based admin. The earlier zero-config Admin.register(Model) panel has been retired; it remains available in pre-1.1 releases (and on the admin-legacy git tag) if you need to reference it.

Getting Started

bun add @zerotal/admin

The panel renders with Flow and flow-ui, and reads/writes through your @zerotal/orm models. @zerotal/auth is an optional peer — needed only if you enable the built-in auth pages.

Register the providers

Add AdminProvider after FlowProvider (which installs the Router.flow() macro and the WebSocket runtime the pages depend on):

// bootstrap/providers.ts
import { FlowProvider } from "@zerotal/flow";
import { AdminProvider } from "@zerotal/admin";

export default [FlowProvider, AdminProvider];

On boot the provider auto-discovers app/admin.ts (where you configure the panel and register resources), then mounts the dashboard, search, notifications, and a List / View / Create / Edit page per resource under the configured path.

Quick start

// app/admin.ts
import { Panel, Resource, text, textInput } from "@zerotal/admin";
import { User } from "./models/User.ts";

Panel.configure({ brand: "Acme", path: "/admin" });

class UserResource extends Resource {
  static model = User;
  static navigationIcon = "users";
  static navigationGroup = "Access";

  static columns() {
    return [
      text("id").sortable(),
      text("name").searchable().sortable(),
      text("email").searchable().copyable(),
      text("role").badge((v) => (v === "admin" ? "primary" : "muted")),
      text("created_at").label("Joined").sortable(),
    ];
  }

  static form() {
    return [textInput("name").required().maxLength(120), textInput("email").email().required()];
  }
}

Panel.register(UserResource);

Visit /admin. You now have a searchable, sortable, paginated table with Create / Edit / View / Delete — and the form above on the Create and Edit pages.

Danger — The panel is unguarded by default (fine for local exploration). Set middleware in your config before shipping; see Securing the panel.

Configuration

Configure the panel with Panel.configure(...) in app/admin.ts, or by exporting an admin config object the provider merges on boot.

Panel.configure({
  path: "/admin",
  brand: "Acme",
  tagline: "Control panel",
  middleware: [AdminGuard], // guard every panel route
  userMenu: {
    // top-bar identity dropdown
    label: "Jane Doe",
    items: [
      { label: "Profile", href: "/admin/profile", icon: "users" },
      { label: "Sign out", href: "/admin/logout", icon: "logout" },
    ],
  },
  theme: {/* see Theming */},
  auth: {/* see Auth pages */},
});
FieldDefaultDescription
path"/admin"URL prefix the panel mounts under.
brand"Zerotal"Sidebar + login heading.
tagline"Admin"Small text under the brand.
middleware[]Middleware guarding every panel route. Set this before production.
userMenuTop-bar dropdown: { label?, items: [{ label, href, icon? }] }.
themeCDNStyling source — Tailwind Play CDN or a prebuilt stylesheet.
authBuilt-in login / profile / reset / verify pages.
authorizeDecide the abilities pages and contributions name.
plugins{}Switch contributing packages off by id, e.g. { monitor: false }.

The rest of the guide

PageWhat it covers
ResourcesDeclare a resource and get list, create, edit, and view screens for a model.
TablesColumns, filters, sorting, search, and bulk actions on the list screen.
Forms & InfolistsBuild create and edit forms, and lay out the read-only view screen.
Actions & RelationsRow, bulk, and page actions, related-record managers, and soft-delete handling.
Panel StructureClusters, nested and singular resources, and running more than one panel.
Dashboard & NavigationWidgets, global search, the command palette, notifications, and the nav tree.
Extending the UICustom cells and controls, render hooks, custom data sources, table layouts.
OperationsHistory, impersonation, saved views, media, roles, and per-user dashboards.
Custom Pages & PluginsAdd your own pages, and contribute pages or widgets to the panel from a package.
Auth Pages & ThemingThe built-in login and profile screens, and how to restyle the panel.
Testing the Admin PanelDrive panel screens in tests and assert on what they render.
ReferencesEvery resource, table, form, and action API in one table.

Next steps

  • Flow — the reactivity layer the pages are built on.
  • Components — the flow-ui kit the panel renders with.
  • ORM — the models, relations, and soft-deletes the panel reads.
  • Authentication / Authorization — the auth pages and can() policies.