Docs
Developer Documentation

Dashboard Pages

Add fully dynamic pages to the web dashboard from your extension: pages, React components, and server actions.

The dashboard is a separate Next.js app that Kora builds and runs for you. Extensions add to it entirely from their own folder: a page declares a nav entry, a component is the React it renders, and a route is a server action the component calls. Kora copies your dashboard/components and dashboard/routes into the dashboard at build time and wires each page to its component through a generated registry, so you never edit the dashboard itself.

The dashboard is a paid add-on. Extension pages only appear when the running instance has the Dashboard add-on and the dashboard has been built. See the Dashboard Setup guide.

Pages

A page is a small metadata class (loaded by Kora, served to the dashboard as nav). It points at a component by name. Pages load from every page.js at any depth under src/dashboard/pages/.

src/dashboard/pages/products/page.js
import { Pages } from "#kora/extensions";

export class Products extends Pages {
    constructor(kora) {
        super(kora, {
            name: "Products",         // nav label
            path: "/products",        // dashboard URL
            icon: "package",          // a Tabler icon name
            order: 2,                 // position in the sidebar
            component: "products",    // the component file to render
            config: {}                // arbitrary props passed to the component
        });
    }
}

component is the base filename of a file in src/dashboard/components. config is handed to that component as a prop, so one component can back several pages with different settings.

Components

A component is a normal React (TSX) file. It must default-export the component the page renders. It receives the page (including its config) as a prop.

src/dashboard/components/products.tsx
import { Stack, Title, Text } from "@mantine/core";
import type { PageMeta } from "@/lib/kora";
import { listProducts } from "../routes/products";

export default async function Products({ page }: { page: PageMeta }) {
    const products = await listProducts();

    return (
        <Stack gap="lg">
            <Title order={2}>{page.name}</Title>
            {products.map(product => (
                <Text key={product.name}>{product.name}</Text>
            ))}
        </Stack>
    );
}

Two import rules keep components portable once they are copied into the dashboard:

  • Shared dashboard building blocks (the Kora HTTP client, layout helpers, shared UI) come from the dashboard itself via the @/ alias, for example @/lib/kora, @/components/codeBlock, @/actions/util.
  • Your own components and routes are referenced relatively, for example ../routes/products or ./productsTable.

Only the file named by a page needs a default export. Supporting components it imports (tables, modals, and so on) can live alongside it and use ordinary named exports.

Routes

Dashboard routes are server actions: server-side functions your components call to read or change data, usually by talking to your extension's HTTP API through the shared Kora client.

src/dashboard/routes/products.ts
"use server";

import { kora } from "@/lib/kora";
import { perform } from "@/actions/util";

export async function listProducts() {
    return kora.products.list();
}

export async function createProduct(name: string, price: number) {
    return perform(() => kora.products.create(name, { price }));
}

Because components and routes are copied into the dashboard when it builds, changing them takes effect on the next dashboard build. Kora detects changes to any dashboard/ file and rebuilds automatically on startup; while developing, run with DASHBOARD.DEV_MODE and restart Kora to pick up edits.

On this page