Skip to main content
zerotal

Auth pages

Opt in with Panel.auth({...}) (or Panel.configure({ auth: {...} })). The pages live behind the @zerotal/admin/auth subpath, so the @zerotal/auth dependency stays optional unless you enable them.

import { Panel } from "@zerotal/admin";

Panel.auth({
  enabled: true,
  identifier: "email", // credential column
  remember: true,
  redirectTo: "/admin",
  authenticateWhen: (u) => (u as { active?: boolean }).active === true,

  // Optional — mounts the forgot/reset pages when provided:
  passwordReset: {
    sendResetLink: (email) => PasswordBroker.sendResetLink({ email }),
    reset: (input) => PasswordBroker.reset(input),
  },
  // Optional — mounts the verify page + profile banner when provided:
  emailVerification: {
    isVerified: (u) => (u as { email_verified_at?: unknown }).email_verified_at != null,
    resend: (u) =>
      (
        u as { sendEmailVerificationNotification(): Promise<void> }
      ).sendEmailVerificationNotification(),
  },
});
PageRouteUses
LoginPage/loginAuth.attempt (+ remember, authenticateWhen)
ProfilePage/profileupdate details · change password · sign out
ForgotPasswordPage/forgot-passwordpasswordReset.sendResetLink
ResetPasswordPage/reset-passwordpasswordReset.reset
VerifyEmailPage/verify-emailemailVerification.resend

Guest screens (login / forgot / reset) mount outside the panel guard so unauthenticated users can reach them; profile / verify mount behind it (override with guestMiddleware / authMiddleware). The 2FA challenge step is left to your auth middleware — the rest of the flow ships here.

Securing the panel

The panel is public until you set guard middleware. A typical setup:

Panel.configure({ middleware: [AuthMiddleware, RequireRoleMiddleware.with("admin")] });
Panel.auth({ enabled: true }); // login lives outside that guard automatically

Theming

By default the panel themes everything with the Tailwind Play CDN plus shadcn-style design tokens and a no-flash dark/light script — zero build step. The toggle in the top bar flips .dark on <html> and persists the choice.

To ship a real build, point theme.stylesheet at your compiled CSS (the CDN is then dropped) and reuse the exported config + tokens so your build matches the default look:

import { Panel, adminTailwindConfig, adminTokensCss } from "@zerotal/admin";

Panel.configure({
  theme: {
    stylesheet: "/assets/admin.css", // your prebuilt Tailwind output
    // cdn: true,                      // keep both during migration
    // tokensCss: ":root { --primary: 270 90% 60%; }",  // override tokens
    // noFonts: true,                  // self-host Inter
  },
});

adminTailwindConfig() returns the token→CSS-var Tailwind config and adminTokensCss() the :root/.dark custom properties — feed both into your own tailwind.config so a compiled stylesheet renders identically.

Next steps

  • Admin overview — the guide's front page and the rest of the sections.
  • Reference — the full API surface in one table.