Personal Dashboard
Personal account dashboard implementation and customization.
The personal dashboard is the landing page users see after signing in. By default, MakerKit provides a mock dashboard at /dashboard with placeholder widgets and sample data. Replace the mock components with your application's key metrics, recent activity, and quick actions to create a personalized home screen.
The personal dashboard is the authenticated user's home page, displaying user-specific data and quick access to common actions.
- Location:
apps/web/app/[locale]/(internal)/dashboard/page.tsx - Route:
/dashboard
This page gives you a starting point from which you can build the home page of your application.

Customizing the Dashboard
The default dashboard uses mock data to demonstrate the layout. Replace the mock components with real data fetching to build your application's home screen.
Replace Mock Data
The dashboard page is a React Server Component. Fetch real data using a loader pattern:
// apps/web/app/[locale]/(internal)/dashboard/page.tsximport { getSupabaseServerClient } from '@kit/supabase/server-client';async function DashboardPage() { const client = getSupabaseServerClient(); // Fetch user-specific data const { data: recentActivity } = await client .from('activity_log') .select('*') .order('created_at', { ascending: false }) .limit(10); return <DashboardContent activity={recentActivity} />;}Add Dashboard Widgets
Create widget components that accept data as props. Keep data fetching in the page, rendering in components:
// _components/stats-widget.tsxinterface StatsWidgetProps { title: string; value: number; change: number;}export function StatsWidget({ title, value, change }: StatsWidgetProps) { return ( <Card> <CardHeader> <CardTitle>{title}</CardTitle> </CardHeader> <CardContent> <div className="text-2xl font-bold">{value}</div> <p className="text-muted-foreground"> {change > 0 ? '+' : ''}{change}% from last month </p> </CardContent> </Card> );}Parallel Data Fetching
When loading multiple widgets, fetch data in parallel to avoid waterfall requests:
async function DashboardPage() { const client = getSupabaseServerClient(); // Parallel fetching - all requests start simultaneously const [stats, activity, notifications] = await Promise.all([ loadDashboardStats(client), loadRecentActivity(client), loadNotifications(client), ]); return ( <DashboardLayout> <StatsWidget data={stats} /> <ActivityFeed activity={activity} /> <NotificationList notifications={notifications} /> </DashboardLayout> );}Common Pitfalls
- Fetching data client-side when server-side works: Dashboard data should load in the server component via a loader function. Client-side fetching adds loading spinners and hurts perceived performance.
- N+1 queries in dashboard widgets: If you have multiple widgets, use
Promise.all()to fetch data in parallel rather than sequentially. - Showing stale data after mutations: After user actions (like creating a resource), use
revalidatePath('/dashboard')in your server action to refresh dashboard data. - Hardcoding user context: Use
getSession()orgetAccountContext()to get the current user - don't pass user IDs through URL params on the dashboard.
Next: Profile Settings →