---
title: "API: Panels and navigation"
description: "Public TypeScript declarations verified against the package export entries."
---

> Documentation Index
> Fetch the complete documentation index at: https://pupitre.rikology.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API: Panels and navigation

This reference is generated before development, checking and building from declarations reachable through the package's published entry points, following re-exports. Internal source-module exports are omitted. Types shown together share their source module; follow the source link for imports and implementation. Use the guides for examples and runtime behavior.

## src/builder/panel.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/builder/panel.ts)

```ts
export type TenancyInput = Partial<Omit<TenancyDefinition, 'mode'>> & {
    mode: TenancyMode;
};

export type PanelBuilder = {
    readonly definition: PanelDefinition;
    impersonation(options?: ImpersonationOptions): PanelBuilder;
    strictAuthorization(enabled?: boolean): PanelBuilder;
    environmentIndicator(options?: EnvironmentIndicatorOptions): PanelBuilder;
    path(value: string): PanelBuilder;
    domain(value: string): PanelBuilder;
    guard(value: string): PanelBuilder;
    middleware(value: PanelDefinition['middleware']): PanelBuilder;
    resources(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    pages(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    brand(value: NonNullable<PanelDefinition['brand']>): PanelBuilder;
    theme(value: PanelThemeDefinition): PanelBuilder;
    timezone(value: string): PanelBuilder;
    locale(value: string): PanelBuilder;
    locales(value: readonly string[]): PanelBuilder;
    breadcrumbs(value: boolean): PanelBuilder;
    /** Serve the panel login/logout pages; `false` keeps the host's own auth. */
    login(enabled?: boolean): PanelBuilder;
    registration(enabled?: boolean): PanelBuilder;
    passwordReset(enabled?: boolean): PanelBuilder;
    emailVerification(enabled?: boolean): PanelBuilder;
    mfa(options?: {
        required?: boolean;
        issuer?: string;
    } | false): PanelBuilder;
    passkeys(enabled?: boolean): PanelBuilder;
    profile(options?: boolean | {
        avatar?: boolean;
    }): PanelBuilder;
    sessions(options?: {
        history?: boolean;
        retentionDays?: number;
    }): PanelBuilder;
    social(providers: string[] | {
        providers: string[];
        registration?: boolean;
    }): PanelBuilder;
    navigation(entries: readonly Definable<NavEntryDefinition>[]): PanelBuilder;
    userMenu(items: readonly UserMenuItemDefinition[]): PanelBuilder;
    tenancy(options: TenancyInput | false): PanelBuilder;
    notifications(options?: {
        interval?: number;
    } | false): PanelBuilder;
    globalSearch(options?: {
        keyBinding?: string;
    } | false): PanelBuilder;
    widgets(value: readonly unknown[] | Record<string, unknown>): PanelBuilder;
    filters(value: readonly WidgetFilterDefinition[]): PanelBuilder;
    /** Plugins applied in order at boot (`definePlugin`). Replaces earlier `.plugins()` calls. */
    plugins(value: readonly PupitrePlugin[]): PanelBuilder;
    toDefinition(): PanelDefinition;
    toJSON(): string;
};

export function definePanel(id: string): PanelBuilder;
```

## src/builder/nav.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/builder/nav.ts)

```ts
export type NavItemBuilder = {
    readonly definition: NavItemDefinition;
    resource(slug: string): NavItemBuilder;
    page(slug: string): NavItemBuilder;
    url(url: string): NavItemBuilder;
    external(enabled?: boolean): NavItemBuilder;
    icon(value: string): NavItemBuilder;
    sort(value: number): NavItemBuilder;
    badge(value: Badge): NavItemBuilder;
    visible(value: Visible): NavItemBuilder;
    toDefinition(): NavItemDefinition;
};

export type NavGroupBuilder = {
    readonly definition: NavGroupDefinition;
    items(values: readonly (string | Definable<NavItemDefinition>)[]): NavGroupBuilder;
    icon(value: string): NavGroupBuilder;
    sort(value: number): NavGroupBuilder;
    collapsible(collapsed?: boolean): NavGroupBuilder;
    toDefinition(): NavGroupDefinition;
};
```

## src/definition/panel.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/definition/panel.ts)

```ts
/** Built-in auth pages served by the panel (WP17). All default to disabled. */
export type PanelAuthDefinition = {
    login: boolean;
    registration: boolean;
    passwordReset: boolean;
    emailVerification: boolean;
    profile: {
        enabled: boolean;
        avatar: boolean;
    };
    mfa?: PanelMfaDefinition;
    passkeys?: PanelPasskeysDefinition;
};

export type PanelThemeDefinition = ThemeDefinition;

export type EnvironmentIndicatorOptions = {
    visible?: boolean | ((ctx: HttpContext) => boolean | Promise<boolean>);
    label?: string;
    color?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'gray';
};

export type ImpersonationOptions = {
    canImpersonate?: (actor: LucidRow, ctx: HttpContext) => boolean | Promise<boolean>;
    canBeImpersonated?: (target: LucidRow, ctx: HttpContext) => boolean | Promise<boolean>;
    redirectTo?: string;
};

export type PanelDefinition = {
    id: string;
    impersonation?: ImpersonationOptions;
    strictAuthorization?: boolean;
    environmentIndicator?: EnvironmentIndicatorOptions;
    path: string;
    brand?: {
        name: string;
        logo?: string;
        logoDark?: string;
        favicon?: string;
    };
    /** Route-group domain, e.g. `admin.example.com` or `:tenant.example.com`. */
    domain?: string;
    guard?: string;
    middleware: readonly (MiddlewareFn | ParsedNamedMiddleware)[];
    resources: readonly unknown[] | Record<string, unknown>;
    /** Custom pages (`definePage`), same shape as `resources` (array or lazy barrel). */
    pages: readonly unknown[] | Record<string, unknown>;
    /** Dashboard widget definitions registered on this panel. */
    widgets?: readonly unknown[] | Record<string, unknown>;
    dashboardFilters?: readonly WidgetFilterDefinition[];
    theme?: PanelThemeDefinition;
    timezone?: string;
    locale?: string;
    locales?: readonly string[];
    breadcrumbs: boolean;
    auth: PanelAuthDefinition;
    sessions?: {
        history: boolean;
        retentionDays: number;
    };
    social?: PanelSocialDefinition;
    /** Explicit navigation; when absent, nav is derived from resources/pages. */
    navigation?: NavEntryDefinition[];
    /**
     * Navigation contributed by plugins. Merged into `navigation` when the panel
     * declares one; otherwise rendered after the derived navigation, and the
     * resources/pages it references are not derived a second time.
     */
    pluginNavigation?: NavEntryDefinition[];
    /** Plugins in application order (WP36); applied by the manager at boot. */
    plugins: readonly PupitrePlugin[];
    userMenu: UserMenuItemDefinition[];
    tenancy?: TenancyDefinition;
    /** Database notifications tray + polling interval (ms); `null` disables. */
    notifications: {
        interval: number;
    } | null;
    /** Global search; `null` disables. */
    globalSearch: {
        keyBinding: string;
    } | null;
    toJSON(): string;
};
```

## src/definition/navigation.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/definition/navigation.ts)

```ts
/**
 * One navigation entry. `resource` / `page` reference a registered resource
 * or custom page by slug; `url` is a literal link.
 */
export type NavItemDefinition = {
    type: 'item';
    label?: string;
    resource?: string;
    page?: string;
    url?: string;
    icon?: string;
    external?: boolean;
    sort?: number;
    badge?: string | number | NavClosure<string | number | null | undefined>;
    visible?: boolean | NavClosure<boolean>;
};

export type NavGroupDefinition = {
    type: 'group';
    label: string;
    icon?: string;
    sort?: number;
    collapsible: boolean;
    collapsed: boolean;
    items: NavItemDefinition[];
};

export type NavEntryDefinition = NavGroupDefinition | NavItemDefinition;

/** Entry of the user menu in the topbar (in addition to built-in profile/logout). */
export type UserMenuItemDefinition = {
    label: string;
    url: string;
    icon?: string;
    method?: 'get' | 'post';
    external?: boolean;
    sort?: number;
    visible?: boolean | NavClosure<boolean>;
};
```

Source: https://pupitre.rikology.com/reference/api-panel/index.mdx
