@zk-libs/zk-dashboard
zk-dashboard
Reusable Angular dashboard shell framework published as @zk-libs/zk-dashboard.
Install
Install the package from the npm registry with its internal ZK package peers:
npm install @zk-libs/zkui @zk-libs/zk-block @zk-libs/zk-dashboard
@zk-libs/zk-dashboard expects Angular 21 and RxJS 7. Import ZKUI theme styles once in the application global stylesheet:
@import '@zk-libs/zkui/theme/styles.css';
Datatable Ownership
@zk-libs/zk-dashboard does not export its own datatable component. The
dashboard package owns shell behaviour: chrome, navigation, route metadata,
access filtering, preferences, adapters, and layout state. Datatable primitives
belong in @zk-libs/zkui/data, and composed admin table sections belong in
@zk-libs/zk-block.
Use the primitive for app-owned filtering, sorting, pagination, row selection, cell templates, and row actions:
import { ZkDataTableComponent } from '@zk-libs/zkui/data';
Use a block when a dashboard page needs a ready admin table section:
import { ZkDataTableAdvancedBlockComponent } from '@zk-libs/zk-block';
Dashboard pages should keep data fetching, permissions, routes, and domain row models in the consuming app. The dashboard shell can frame the page and derive breadcrumbs/navigation from route metadata, but it should not own table data APIs or consumer-specific record workflows.
Basic Shell Setup
Register the router and dashboard provider in application config:
import { ApplicationConfig } from '@angular/core';
import { provideRouter, Routes } from '@angular/router';
import {
provideZkDashboard,
ZK_DASHBOARD_ROUTE_DATA_KEY,
ZkDashboardShellComponent,
} from '@zk-libs/zk-dashboard';
const routes: Routes = [
{
path: '',
component: ZkDashboardShellComponent,
children: [
{
path: 'overview',
loadComponent: () => import('./overview.page').then((m) => m.OverviewPage),
title: 'Overview',
data: {
[ZK_DASHBOARD_ROUTE_DATA_KEY]: {
breadcrumb: 'Overview',
navigation: { icon: 'info', label: 'Overview', order: 1 },
},
},
},
],
},
];
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideZkDashboard({
breadcrumbs: [{ href: '/overview', label: 'Dashboard' }],
navigationSource: 'routes',
title: 'Operations portal',
user: {
email: 'operator@example.com',
initials: 'OP',
name: 'Operator',
permissions: ['dashboard:read'],
role: 'Operations',
},
}),
],
};
Route Data Contract
Use data[ZK_DASHBOARD_ROUTE_DATA_KEY] for route-level shell behaviour:
{
path: 'full-screen',
component: FullScreenPage,
data: {
[ZK_DASHBOARD_ROUTE_DATA_KEY]: {
breadcrumb: false,
contentWidth: 'fixed',
hideFooter: true,
hideHeader: true,
hideSidebar: true,
requiredPermissions: ['admin:read'],
},
},
}
The same metadata can derive menus with navigationSource: 'routes'. Use navigationSource: 'config' when you want explicit menu configuration, or merged when a consumer app needs both.
Sidebar Selectors
Use sidebar selectors for reusable scope switching, such as workspaces, organisations, projects, tenants, regions, environments, products, pinned scopes, and recently used entities:
provideZkDashboard({
sidebar: {
selectors: [
{
id: 'workspace',
label: 'Workspace',
ariaLabel: 'Select workspace',
placement: 'nav-top',
selectedId: 'alpha',
searchable: true,
searchPlaceholder: 'Search workspaces...',
preserveRoutePattern: true,
preserveQueryParams: true,
preserveFragment: true,
items: [
{
id: 'alpha',
label: 'Alpha Workspace',
description: 'Current operations scope',
badge: 'Pinned',
badgeTone: 'info',
route: '/workspace/alpha',
statusText: 'Active',
statusTone: 'success',
},
],
footerAction: {
id: 'all-workspaces',
label: 'All workspaces',
route: '/workspaces',
},
},
],
},
});
Async item loading stays app-owned:
await dashboardState.loadSidebarSelectorItems(
'workspace',
async ({ abortSignal }) => workspaceApi.list({ abortSignal }),
);
The shell exposes loading, reloading, empty, error, reload, and resolved states. The runtime does not fetch browser-relative URLs during server render.
Route-preserving selectors can move between scoped routes, for example
/workspace/alpha/settings to /workspace/beta/settings. Use
routeBuilder when a consumer needs custom parameter replacement. Selectors,
items, and footer actions all support requiredPermissions.
Advanced item rendering can use customContent with a template or component
outlet while the dashboard shell keeps keyboard handling, selected state, and
access filtering. Override shellLabels.sidebarSelector* values for all
runtime copy, including search, empty, loading, count, selected, and reload
labels.
Access And Preferences
Use canActivateZkDashboardRoute for route permission checks and *zkCanAccess for template-level visibility:
import { canActivateZkDashboardRoute } from '@zk-libs/zk-dashboard';
{ path: 'admin', canActivate: [canActivateZkDashboardRoute], component: AdminPage }
Sidebar collapse persistence is opt-in and SSR-safe by default:
import {
provideZkDashboard,
provideZkDashboardLocalStoragePreferences,
} from '@zk-libs/zk-dashboard';
providers: [
provideZkDashboard({
preferences: { persistSidebarCollapsed: true },
}),
provideZkDashboardLocalStoragePreferences(),
];
Package Boundary
Main imports expose only runtime framework APIs. Demo screens, demo routes, and placeholder pages belong in the consumer application or docs app, not in the npm package API.
zk-dashboard depends on published @zk-libs/zkui and @zk-libs/zk-block packages. Do not import from local source paths in consuming applications.
Release Checks
Before publishing, run:
npm run check
npx nx run zk-dashboard:build
npm run smoke:registry
cd dist/libs/zk-dashboard
npm publish --dry-run
Publish order is @zk-libs/zkui, then @zk-libs/zk-block, then @zk-libs/zk-dashboard.