Recipe: Custom Dashboard
Overview
Section titled “Overview”Build a custom dashboard view that displays analytics alongside your admin panel.
Create the Dashboard Component
Section titled “Create the Dashboard Component”import { useRebaseContext } from "@rebasepro/app";import { useEffect, useState } from "react";
function DashboardView() { const context = useRebaseContext(); const [stats, setStats] = useState({ totalOrders: 0, totalRevenue: 0, activeProducts: 0, recentOrders: [] });
useEffect(() => { async function loadStats() { // Use the data source to fetch aggregate data // `find` resolves to { data, meta } — the rows are on `data`, and they are // flat, so it is `o.total` rather than `o.values.total`. const { data: orders } = await context.data .collection<{ total: number }>("orders") .find({ limit: 1000 });
const { data: products } = await context.data .collection<Record<string, unknown>>("products") .find({ where: { active: ["==", true] } });
setStats({ totalOrders: orders.length, totalRevenue: orders.reduce((sum, o) => sum + (o.total ?? 0), 0), activeProducts: products.length, recentOrders: orders.slice(0, 5) }); } loadStats(); }, []);
return ( <div className="p-8"> <h1 className="text-xl font-semibold tracking-[-0.01em] mb-6">Dashboard</h1> <div className="grid grid-cols-3 gap-4 mb-8"> <StatCard title="Total Orders" value={stats.totalOrders} /> <StatCard title="Revenue" value={`$${stats.totalRevenue.toFixed(2)}`} /> <StatCard title="Active Products" value={stats.activeProducts} /> </div> <h2 className="text-sm font-semibold tracking-[-0.01em] mb-4">Recent Orders</h2> <ul> {stats.recentOrders.map(order => ( <li key={order.id}> Order #{order.id} — ${order.values.total} </li> ))} </ul> </div> );}
function StatCard({ title, value }: { title: string; value: string | number }) { return ( <div className="bg-surface-100 dark:bg-surface-800 rounded-lg p-6"> <p className="text-xs text-surface-500">{title}</p> <p className="text-xl font-semibold">{value}</p> </div> );}Register as a Custom View
Section titled “Register as a Custom View”const views: AppView[] = [ { slug: "dashboard", name: "Dashboard", view: <DashboardView />, admin: { icon: "dashboard", group: "Analytics" } }];Pass it to the navigation controller:
const navigationStateController = useBuildNavigationStateController({ views, collections: () => collections, // These four are required — the controller resolves navigation against them. authController, data, collectionRegistryController, urlController});The dashboard now appears in the sidebar under “Analytics” and is accessible at /dashboard.
Pinning the group to the bottom
Section titled “Pinning the group to the bottom”Groups named "Admin" or "Settings" sink below the others in the drawer, by
string comparison on the name. That is easy to lose — translate the label and
the ordering silently stops happening — so say it explicitly instead:
{ slug: "dashboard", name: "Dashboard", view: <DashboardView />, group: "Ajustes", pinToBottom: true }Setting pinToBottom on any one view in a group pins the whole group, since
the drawer orders groups rather than individual views.
Navigating from a custom view
Section titled “Navigating from a custom view”A view component receives no props. To route somewhere — another custom view, a
collection, an entity — reach for useUrlController, which is exported from
@rebasepro/cms:
import { useUrlController } from "@rebasepro/cms";
function DashboardView() { const urlController = useUrlController();
return ( <> <button onClick={() => urlController.navigate(urlController.buildAppUrlPath("reports"))}> Reports </button> <button onClick={() => urlController.navigate(urlController.buildUrlCollectionPath("orders"))}> All orders </button> <button onClick={() => urlController.navigate(urlController.buildUrlCollectionPath("orders/B34SAP8Z"))}> Order B34SAP8Z </button> </> );}Build the path rather than hard-coding it: collection URLs are prefixed
(orders → /c/orders) and the prefix is not part of the public contract.
useSidePanel opens an entity in the side panel instead of navigating, which is
usually what you want for a row in a list.
Adding Charts
Section titled “Adding Charts”Install a charting library:
pnpm add rechartsThen use it in your dashboard:
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from "recharts";
function RevenueChart({ data }) { return ( <LineChart width={600} height={300} data={data}> <CartesianGrid strokeDasharray="3 3" /> <XAxis dataKey="date" /> <YAxis /> <Tooltip /> <Line type="monotone" dataKey="revenue" stroke="#8884d8" /> </LineChart> );}Next Steps
Section titled “Next Steps”- Custom Views — Frontend overview
- Hooks Reference — Available hooks
