The DashboardNavigation component provides a centralized, role-based navigation solution for dashboard pages with autonomous permission checking and extensive customization through slots.
Overview
Section titled “Overview”DashboardNavigation extracts navigation logic from page layouts into a standalone, reusable component that handles role-based visibility, permission checking, and custom navigation items through a flexible slot system.
🎯 Autonomous
Self-contained permission checking—no external permission props required.
🔌 Slot-Based
Three customization slots (before, default, after) for flexible navigation layouts.
🛡️ Role-Based
Automatic visibility control based on user roles (admin, team_admin, team_manager).
⚡ Performance
Parallel permission checks with optional pre-computed permissions for layouts.
Default Navigation Items
Section titled “Default Navigation Items”The component renders these navigation items by default:
| Item | URL | Required Role | Always Visible |
|---|---|---|---|
| Orders | /dashboard/orders | team_admin | ❌ |
| Events | /dashboard/events | admin | ❌ |
| Users | /dashboard/users | team_admin | ❌ |
| Clients | /dashboard/clients | team_manager | ❌ |
Basic Usage
Section titled “Basic Usage”Simple Implementation
Section titled “Simple Implementation”The simplest usage requires no props—the component handles all permission checking internally:
---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'---
<nav> <DashboardNavigation /></nav>With Pre-Computed Permissions (Recommended for Layouts)
Section titled “With Pre-Computed Permissions (Recommended for Layouts)”For better performance in layouts where permissions are already computed, pass them to avoid redundant checks:
---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'import { canViewContent } from '#utils/role-guard'
// Compute permissions onceconst [canEditUsers, canEditClients, canEditProducts] = await Promise.all([ canViewContent(Astro.locals, ['team_admin']), canViewContent(Astro.locals, ['team_manager']), canViewContent(Astro.locals, ['team_admin', 'admin']),])---
<DashboardNavigation permissions={{ canEditUsers, canEditClients, canEditProducts, }}/>Customization
Section titled “Customization”Hiding Specific Items
Section titled “Hiding Specific Items”Use the hideItems prop to hide specific navigation items:
<DashboardNavigation hideItems={['events', 'users']} />This would show only Orders and Clients (if user has access).
Adding Custom Items with Slots
Section titled “Adding Custom Items with Slots”The component provides three slots for customization:
Before Slot
Section titled “Before Slot”Add items before the default navigation:
<DashboardNavigation> <li slot="before"> <Link.LinkButton href="/dashboard/home"> <Icon name="home" /> Home </Link.LinkButton> </li></DashboardNavigation>After Slot
Section titled “After Slot”Add items after the default navigation:
<DashboardNavigation> <li slot="after"> <Link.LinkButton href="/dashboard/settings"> <Icon name="settings" /> Settings </Link.LinkButton> </li></DashboardNavigation>Default Slot (Replace All Defaults)
Section titled “Default Slot (Replace All Defaults)”Replace all default navigation items with custom ones:
<DashboardNavigation hideDefaults={true}> <li> <Link.LinkButton href="/custom-dashboard">Custom</Link.LinkButton> </li> <li> <Link.LinkButton href="/reports">Reports</Link.LinkButton> </li></DashboardNavigation>Combining Slots
Section titled “Combining Slots”You can combine multiple slots for maximum flexibility:
<DashboardNavigation hideItems={['users']}> <li slot="before"> <Link.LinkButton href="/dashboard/overview">Overview</Link.LinkButton> </li>
<!-- Default items (Orders, Events, Clients) render here -->
<li slot="after"> <Link.LinkButton href="/dashboard/analytics">Analytics</Link.LinkButton> </li> <li slot="after"> <Link.LinkButton href="/dashboard/settings">Settings</Link.LinkButton> </li></DashboardNavigation>Props Reference
Section titled “Props Reference”Component Props
Section titled “Component Props”type Props = { hideDefaults?: boolean hideItems?: ('orders' | 'events' | 'users' | 'clients' | 'products')[] permissions?: { canEditUsers?: boolean canEditClients?: boolean canEditProducts?: boolean } className?: string debug?: boolean}hideDefaults
Section titled “hideDefaults”- Type:
boolean - Default:
false - Description: Hide all default navigation items. Use with default slot for fully custom navigation.
hideItems
Section titled “hideItems”- Type:
Array<'orders' | 'events' | 'users' | 'clients'> - Default:
[] - Description: Array of specific items to hide from default navigation.
permissions
Section titled “permissions”-
Type:
Object -
Default:
undefined -
Description: Pre-computed permission checks. If not provided, component computes them internally.
canEditUsers: User can view Users (team_admin role)canEditClients: User can view Clients (team_manager role)canEditProducts: User can view Products (team_admin or admin role)
Events is not in this list:
/dashboard/eventsonly redirects signed-out visitors, so its link is shown to every signed-in user rather than gated.
className
Section titled “className”- Type:
string - Default:
"" - Description: Additional CSS classes for the nav element.
- Type:
boolean - Default:
false - Description: Enable console logging for permission checks (useful during development).
| Slot | Description | Example Use Case |
|---|---|---|
before | Content before default items | Add “Home” or “Overview” link |
| default | Replace all navigation | Fully custom navigation |
after | Content after default items | Add “Settings” or “Profile” link |
Examples
Section titled “Examples”Example 1: Admin Dashboard
Section titled “Example 1: Admin Dashboard”---import { Auth } from '#layouts'import DashboardNavigation from '#components/astro/DashboardNavigation.astro'import { Link } from '@fpkit/acss'---
<Auth pageTitle="Admin Dashboard" requiredRoles={['admin']}> <nav> <DashboardNavigation> <li slot="before"> <Link.LinkButton href="/dashboard">Dashboard Home</Link.LinkButton> </li> <li slot="after"> <Link.LinkButton href="/dashboard/reports">Reports</Link.LinkButton> </li> <li slot="after"> <Link.LinkButton href="/dashboard/system">System Settings</Link.LinkButton> </li> </DashboardNavigation> </nav></Auth>Example 2: Client Portal (Limited Access)
Section titled “Example 2: Client Portal (Limited Access)”---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'import { Link } from '@fpkit/acss'---
<DashboardNavigation hideDefaults={true}> <li> <Link.LinkButton href="/portal/overview">Overview</Link.LinkButton> </li> <li> <Link.LinkButton href="/portal/orders">My Orders</Link.LinkButton> </li> <li> <Link.LinkButton href="/portal/support">Support</Link.LinkButton> </li></DashboardNavigation>Example 3: Role-Specific Navigation
Section titled “Example 3: Role-Specific Navigation”---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'import { canViewContent } from '#utils/role-guard'
// Check if user is team managerconst isTeamManager = await canViewContent(Astro.locals, ['team_manager'])---
<DashboardNavigation hideItems={isTeamManager ? [] : ['clients']}> <li slot="after"> <Link.LinkButton href="/dashboard/team">Team</Link.LinkButton> </li></DashboardNavigation>Example 4: Debugging Permissions
Section titled “Example 4: Debugging Permissions”Enable debug mode to see permission check results in the console:
<DashboardNavigation debug={true} />Console output:
DashboardNavigation - Computed permissions: { canEditUsers: false, canEditClients: true, canEditProducts: true}Performance Considerations
Section titled “Performance Considerations”Pre-Computed Permissions
Section titled “Pre-Computed Permissions”When using the component in layouts where permissions are already checked (e.g., Auth layout), pass pre-computed permissions to avoid redundant database queries:
❌ Not Recommended (redundant checks):
---const canEditUsers = await canViewContent(Astro.locals, ['team_admin'])const canEditClients = await canViewContent(Astro.locals, ['team_manager'])const canEditEvents = await canViewContent(Astro.locals, ['admin'])---
<!-- Component will re-check permissions internally --><DashboardNavigation />✅ Recommended (efficient):
---// Compute once, reuse everywhereconst [canEditUsers, canEditClients, canEditProducts] = await Promise.all([ canViewContent(Astro.locals, ['team_admin']), canViewContent(Astro.locals, ['team_manager']), canViewContent(Astro.locals, ['team_admin', 'admin']),])---
<!-- Pass pre-computed permissions --><DashboardNavigation permissions={{ canEditUsers, canEditClients, canEditProducts, }}/>Parallel Permission Checks
Section titled “Parallel Permission Checks”The component uses Promise.all() internally for parallel permission checks, reducing latency by ~66% compared to sequential checks.
Architecture
Section titled “Architecture”Separation of Concerns
Section titled “Separation of Concerns”The DashboardNavigation component follows a clear separation of concerns:
- Component Responsibility: Navigation rendering and role-based visibility
- Layout Responsibility: Page-level access control (requiredRoles guard)
- Parent Flexibility: Optional pre-computed permissions for performance
Integration with Auth Layout
Section titled “Integration with Auth Layout”The Auth layout uses DashboardNavigation for consistent navigation across protected pages:
---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'---
<section class="flex"> <DashboardNavigation /></section>The layout handles page-level protection, while the component handles navigation visibility.
Related Resources
Section titled “Related Resources”Auth Layout Role Guards
Learn about declarative page protection using Auth layout props.
Configurable Roles
Understand the role system and permission hierarchy.
@fpkit/acss Components
Explore the Nav and Link components used internally.
Migration from Auth Layout
Section titled “Migration from Auth Layout”If you’re migrating from the original Auth layout navigation code:
---const [canEditUsers, canEditClients, canEditEvents] = await Promise.all([ canViewContent(Astro.locals, ['team_admin']), canViewContent(Astro.locals, ['team_manager']), canViewContent(Astro.locals, ['admin']),])---
<Nav> <ul> <li> <Link.LinkButton href="/dashboard/orders">Orders</Link.LinkButton> </li> {canEditEvents && ( <li> <Link.LinkButton href="/dashboard/events">Events</Link.LinkButton> </li> )} {canEditUsers && ( <li> <Link.LinkButton href="/dashboard/users">Users</Link.LinkButton> </li> )} {canEditClients && ( <li> <Link.LinkButton href="/dashboard/clients">Clients</Link.LinkButton> </li> )} </ul></Nav>---import DashboardNavigation from '#components/astro/DashboardNavigation.astro'---
<DashboardNavigation />Benefits:
- 40+ lines reduced to 1 line
- No permission checks needed
- Reusable across pages
- Consistent navigation everywhere
Best Practices
Section titled “Best Practices”- Use in Layouts: Import once in your layout component for consistent navigation
- Pre-Compute When Possible: Pass permissions prop when already computed for better performance
- Slot Composition: Use slots for page-specific navigation rather than forking the component
- Hide Strategically: Use
hideItemsto conditionally show/hide based on context - Debug in Development: Enable
debug={true}to troubleshoot permission issues
Troubleshooting
Section titled “Troubleshooting”Navigation Items Not Showing
Section titled “Navigation Items Not Showing”Problem: Expected navigation items don’t appear
Solutions:
- Check user roles in the Auth layout with
<UserInfo showRoleBadge /> - Enable debug mode:
<DashboardNavigation debug={true} /> - Verify role configuration in
config/roles.config.ts
Performance Issues
Section titled “Performance Issues”Problem: Slow navigation rendering
Solutions:
- Pass pre-computed permissions from layout to avoid redundant checks
- Ensure parallel permission checks are working (check network tab)
- Consider caching permission results in middleware
Custom Slots Not Rendering
Section titled “Custom Slots Not Rendering”Problem: Slot content doesn’t appear
Solutions:
- Ensure slot names are correct (
before,after, or no name for default) - When using default slot, set
hideDefaults={true} - Wrap slot content in proper HTML elements (
<li>for list items)
Need help? Check the Auth Layout Role Guards guide or review the component source code.