UIBlocks.
All page recipes

Application · Clean SaaS · 1 block

SaaS dashboard

A complete application dashboard with host-owned metrics, revenue charts, searchable customers, filters, pagination, and service callbacks.

Live page preview

Interactive · 1200px viewport · Scroll inside the preview

Install the complete page

One command installs the page component, its matching blocks, shared helpers, and MIT notices into your configured components directory. Prepare your existing project once, then run the page install command.

Before the first install
  1. 1.

    Open your existing React project

    Use React 19+, TypeScript/TSX, and a working Tailwind CSS 4 build. Keep the project's existing router, dependencies, and global stylesheet.

  2. 2.

    Check components.json before installing

    Reuse an existing components.json. Its aliases.components must resolve in TypeScript and the bundler, and tailwind.css must point to the stylesheet your app loads. Do not reinitialize an already configured project.

    Only if components.json is missing; run inside the existing app and follow its framework setup.

    npx shadcn@latest init
  3. 3.

    Include the installed sources in Tailwind

    Keep the host's standard shadcn theme variables, Tailwind @theme mappings, and base border styles loaded (background, foreground, border, sidebar, and chart tokens). Ensure Tailwind scans the actual installed components directory. Restricted source(none), ignored paths, and monorepos may need an @source path relative to the stylesheet. For theme='dark', use a class-based dark variant in the host CSS.

  4. 4.

    Import through your configured components alias

    The @components/ entry is a registry target placeholder, not a JavaScript import. Replace the example's @/components prefix with components.json aliases.components, verify the file printed by the CLI, and mount it in an existing host route.

Missing styles or class-based dark mode?

Merge only the directives your Tailwind CSS 4 stylesheet needs. This source path example assumes src/styles.css and components installed under src/components; adjust it to your actual directory. Keep your host’s existing dark-mode selector and use theme="inherit", or configure this class variant when using theme="dark".

/* Example: src/styles.css */
/* Add only if this directory is outside your existing scan scope. */
@source "./components/uiblocks";

/* Required for the recipe theme="dark" class switch. */
@custom-variant dark (&:where(.dark, .dark *));

Setup references: shadcn configuration · Tailwind source scanning · Dark mode

Install this page

npx shadcn@latest add https://uiblocks.org/r/recipe-saas-dashboard.json

Import one component

import { useState, type ComponentProps } from "react";
import SaasDashboardRecipe from "@/components/uiblocks/recipe-saas-dashboard/recipe-saas-dashboard";

type CustomerQuery = NonNullable<ComponentProps<typeof SaasDashboardRecipe>["customerQuery"]>;

export default function DashboardPage() {
  const [customerQuery, setCustomerQuery] = useState<CustomerQuery>({
    search: "", filters: { Plan: [], Status: [] }, page: 1, pageSize: 9,
  });
  return (
    <SaasDashboardRecipe
      mode="controlled"
      brand="Your product"
      title="Overview"
      metrics={[]}
      chartData={[]}
      customers={[]}
      customerQuery={customerQuery}
      onCustomerQueryChange={setCustomerQuery}
    />
  );
}

The import example uses the standard @/components alias. Keep your project’s existing alias and use the installed entry printed by shadcn. Pass your own copy, links, and callbacks through the component options below.

Local interface demo · no upload or conversion
import SaasDashboardRecipe from "@/components/uiblocks/recipe-saas-dashboard/recipe-saas-dashboard";

export default function DashboardDemo() {
  return <SaasDashboardRecipe mode="demo" brand="Lumen Labs" />;
}
Component options and callbacks
mode, metrics, chartData, customers
Default controlled mode contains no sample metrics, chart points, or customers. Supply real data; explicit mode='demo' opts in to labeled illustrative records. An explicit empty array stays empty in either mode. chartData points use date, revenue, charges, and optional priorRevenue/priorCharges; no hourly data or prior values are invented for host data.
customerQuery, onCustomerQueryChange, pagination, totalCustomers, customerFilterOptions
Query is {search, filters:{Plan:string[], Status:string[]}, page, pageSize}. Client mode filters and paginates supplied rows locally. Server mode renders supplied current-page rows unchanged and uses totalCustomers for pagination; the host must update rows, total, and query from its service. Search/filter changes reset page to 1. Supply customerFilterOptions={Plan:string[], Status:string[]} for service-wide values across all remote pages; supplied values, current-row values, and selected values remain available. Controlled fallback uses only actual rows and selected values, never demo choices. Never pass the full dataset as a remote page or filter it twice.
period, dateRange, onPeriodChange, onDateRangeChange
Control day/week/month/year and {from,to} ISO date strings. Callbacks let the host fetch actual aggregations; chartData is already aggregated and is not synthesized or filtered again. Controls are disabled in controlled mode without their callback. Dates start blank until the host supplies a range.
loading, error, onRetry, onExport, onCustomerAction
Provide real loading and error state. Retry/refresh delegates to onRetry; export receives the complete CustomerQuery; customer actions receive 'view' or 'contact' and the unchanged row. Handlers can return promises; rejected actions display their actual error and never report success. Business actions without a host handler are disabled in controlled mode. No automatic polling, export, mail, or network request is performed.
brand, title, description, navigation, headerSlot, theme, className
Set real identity, copy, ordinary host navigation URLs, and account controls. The sidebar and layout retain the dashboard block's neutral surfaces, blue chart accent, spacing, and responsive customer table. Inherit the host theme or use a class-based dark variant.

The page, block by block

Included in the complete page bundle. You can also reuse each block individually.

  1. 01Dashboard with Revenue and Customers

Keep the design intact

  • Use real reporting periods and shared host state for metrics, charts, customer query, and pagination. Never present the explicit demo records as production analytics.
  • Supply loading, empty, error, and populated states. Status text must remain understandable without color; keep long customer names and email addresses readable on mobile.
  • In server pagination mode, pass only the current page from the service and totalCustomers. Search and filter callbacks must reset the host page to 1 and fetch the new rows.
  • Connect retry, export, and customer-action callbacks to your authenticated service or host router. Unconnected business controls remain disabled; the library includes no backend or billing integration.
  • Keep the existing dashboard hierarchy, typography, neutral surfaces, and blue chart accent. Verify the installed page with long Chinese headings, sparse chart data, and mobile widths.

Hand this recipe to your AI assistant

Build a SaaS admin dashboard using UIBlocks saas-dashboard. Read get_recipe and get_preview, install recipe-saas-dashboard in one command, and import SaasDashboardRecipe. Keep the existing dashboard layout, type scale, neutral surfaces, and blue chart accent. Default to controlled mode with host-owned metrics, chartData, customers, period/dateRange, loading, error, and CustomerQuery. Connect onRetry, onExport, onCustomerAction, and query/date/period callbacks to the application's authenticated service and router. For server pagination, supply the current rows and totalCustomers without local filtering or slicing. Explicit mode='demo' is for labeled interface inspection only. Check empty/loading/error states, callback rejection, long Chinese copy, and mobile layout; preserve the host routes and aliases.

Read the recipe JSON