---
title: "API: Pages and widgets"
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: Pages and widgets

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/page.ts

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

```ts
export type CustomPageBuilder = {
    readonly definition: CustomPageDefinition;
    path(value: string): CustomPageBuilder;
    title(value: string): CustomPageBuilder;
    icon(value: string): CustomPageBuilder;
    cluster(value: ClusterBuilder): CustomPageBuilder;
    navigation(value: NonNullable<CustomPageDefinition['navigation']>): CustomPageBuilder;
    canAccess(value: NonNullable<CustomPageDefinition['canAccess']>): CustomPageBuilder;
    form(callback: (schema: FormSchema<Record<string, unknown>>) => FormInput): CustomPageBuilder;
    fill(value: NonNullable<CustomPageDefinition['fill']>): CustomPageBuilder;
    save(value: NonNullable<CustomPageDefinition['save']>): CustomPageBuilder;
    actions(value: readonly ActionBuilder[]): CustomPageBuilder;
    component(value: string): CustomPageBuilder;
    widgets(value: readonly Pick<WidgetBuilder, 'toDefinition'>[]): CustomPageBuilder;
    filters(value: readonly WidgetFilterDefinition[]): CustomPageBuilder;
    toDefinition(): CustomPageDefinition;
    toJSON(): string;
};

export function definePage(slug: string): CustomPageBuilder;
```

## src/builder/widget.ts

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

```ts
export type StatBuilder = {
    readonly definition: WidgetStat;
    description(value: string): StatBuilder;
    descriptionIcon(value: string): StatBuilder;
    color(value: string): StatBuilder;
    chart(value: number[]): StatBuilder;
    toDefinition(): WidgetStat;
};

export function stat(label: string, value: string | number): StatBuilder;

export type WidgetBuilder = {
    readonly definition: WidgetDefinition;
    stats(callback: (args: WidgetContext) => StatInput[] | Promise<StatInput[]>): WidgetBuilder;
    timeline(callback: (args: WidgetContext) => TimelineItem[] | Promise<TimelineItem[]>): WidgetBuilder;
    chart<T extends NonNullable<WidgetDefinition['chartType']>>(type: T, callback: (args: WidgetContext) => ChartResult<T> | Promise<ChartResult<T>>): WidgetBuilder;
    table(callback: (schema: TableSchema) => TableSchema): WidgetBuilder;
    component(value: string, loader?: NonNullable<WidgetDefinition['load']>): WidgetBuilder;
    actions(value: readonly ActionBuilder[]): WidgetBuilder;
    span(value: number | 'full'): WidgetBuilder;
    sort(value: number): WidgetBuilder;
    poll(value: string): WidgetBuilder;
    heading(value: string): WidgetBuilder;
    description(value: string): WidgetBuilder;
    canView(value: NonNullable<WidgetDefinition['canView']>): WidgetBuilder;
    lazy(value: boolean): WidgetBuilder;
    toDefinition(): WidgetDefinition;
    toJSON(): string;
};

export function defineWidget(name: string, model?: LucidModel): WidgetBuilder;
```

## src/definition/custom_page.ts

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

```ts
export type CustomPageDefinition = {
    slug: string;
    /** Path relative to the panel (tenant) prefix, e.g. `/settings`. */
    path: string;
    title: string;
    icon?: string;
    cluster?: ClusterDefinition;
    navigation?: {
        group?: string;
        sort?: number;
        label?: string;
        badge?: (ctx: HttpContext) => unknown | Promise<unknown>;
    };
    /** Access gate (nav visibility + 403 on the route). Default: allowed. */
    canAccess?: (ctx: HttpContext) => boolean | Promise<boolean>;
    form?: SchemaNodeDefinition[];
    fill?: (ctx: HttpContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
    save?: (args: {
        data: Record<string, unknown>;
        ctx: HttpContext;
    }) => unknown | Promise<unknown>;
    actions: ActionDefinition[];
    widgets?: WidgetDefinition[];
    filters?: WidgetFilterDefinition[];
    component?: string;
    toJSON(): string;
};
```

## src/definition/widget.ts

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

```ts
export type WidgetStat = {
    label: string;
    value: string | number;
    description?: string;
    descriptionIcon?: string;
    color?: string;
    chart?: number[];
};

export type TimelineItem = {
    title: string;
    description?: string;
    time: string;
    actor?: string;
    icon?: string;
    color?: string;
    changes?: {
        field: string;
        old: unknown;
        new: unknown;
    }[];
};

export type WidgetData = {
    kind: 'stats';
    stats: WidgetStat[];
} | {
    kind: 'chart';
    labels: string[];
    datasets: {
        label: string;
        data: number[];
        color?: string;
    }[];
} | {
    kind: 'scatter';
    datasets: {
        label: string;
        data: {
            x: number;
            y: number;
        }[];
        color?: string;
    }[];
} | {
    kind: 'table';
    data: unknown;
} | {
    kind: 'timeline';
    items: TimelineItem[];
} | {
    kind: 'component';
    data: unknown;
};

export type WidgetContext = {
    ctx: HttpContext;
    filters: Record<string, unknown>;
    record?: unknown;
};

export type WidgetDefinition = {
    name: string;
    kind: 'stats' | 'chart' | 'table' | 'timeline' | 'component';
    load?: (args: WidgetContext) => unknown | Promise<unknown>;
    chartType?: 'line' | 'bar' | 'area' | 'pie' | 'doughnut' | 'radar' | 'scatter';
    table?: TableDefinition;
    model?: LucidModel;
    component?: string;
    actions?: readonly ActionDefinition[];
    heading?: string;
    description?: string;
    span: number | 'full';
    sort: number;
    poll?: string;
    canView?: (ctx: HttpContext) => boolean | Promise<boolean>;
    lazy: boolean;
    toJSON(): string;
};
```

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