# Application Navigation in Tanstack Start Supabase

> Configure the main application sidebar navigation, layout style, and menu structure in the Tanstack Start Supabase SaaS Kit.

*Canonical: https://makerkit.dev/docs/tanstack-supabase/configuration/personal-account-sidebar-configuration*

---

The application navigation at `apps/web/config/navigation.config.tsx` defines the main sidebar menu for the dashboard shell. A single, slug-free config serves both personal and team accounts — the active account is resolved from the database, so the same routes work in every workspace. Add your own routes here to extend the dashboard navigation.

{% alert type="default" title="Where to Add Routes" %}
This is the file you'll edit most often when building your product. Add dashboard pages and feature-specific navigation items here. Settings-section items live in a separate config — see [Settings Navigation](/docs/tanstack-supabase/configuration/team-account-sidebar-configuration).
{% /alert %}

## Layout Options

| Variable | Options | Default | Description |
|----------|---------|---------|-------------|
| `VITE_USER_NAVIGATION_STYLE` | `sidebar`, `header` | `sidebar` | Navigation layout style |
| `VITE_HOME_SIDEBAR_COLLAPSED` | `true`, `false` | `false` | Start with collapsed sidebar |
| `VITE_SIDEBAR_COLLAPSIBLE_STYLE` | `offcanvas`, `icon`, `none` | `icon` | How sidebar collapses |

### Sidebar Style (Default)

```bash
VITE_USER_NAVIGATION_STYLE=sidebar
```

Shows a vertical sidebar on the left with expandable sections.

### Header Style

```bash
VITE_USER_NAVIGATION_STYLE=header
```

Shows navigation in a horizontal header bar.

### Collapse Behavior

Control how the sidebar behaves when collapsed:

```bash
# Icon mode: Shows icons only when collapsed
VITE_SIDEBAR_COLLAPSIBLE_STYLE=icon

# Offcanvas mode: Slides in/out as an overlay
VITE_SIDEBAR_COLLAPSIBLE_STYLE=offcanvas

# None: Sidebar cannot be collapsed
VITE_SIDEBAR_COLLAPSIBLE_STYLE=none
```

## Default Configuration

The kit ships with a single dashboard entry:

```tsx
import { Home } from 'lucide-react';
import * as z from 'zod';

import { NavigationConfigSchema } from '@kit/ui/navigation-schema';

import pathsConfig from '#/config/paths.config.ts';

const iconClasses = 'w-4';

const routes = [
  {
    label: 'common.routes.application',
    children: [
      {
        label: 'common.routes.dashboard',
        path: pathsConfig.app.home,
        Icon: <Home className={iconClasses} />,
        highlightMatch: `${pathsConfig.app.home}$`,
      },
    ],
  },
] satisfies z.output<typeof NavigationConfigSchema>['routes'];

export const workspaceNavigationConfig = NavigationConfigSchema.parse({
  routes,
  style: import.meta.env.VITE_USER_NAVIGATION_STYLE,
  sidebarCollapsed: import.meta.env.VITE_HOME_SIDEBAR_COLLAPSED,
  sidebarCollapsedStyle: import.meta.env.VITE_SIDEBAR_COLLAPSIBLE_STYLE,
});
```

## Adding Custom Routes

### Simple Route

Add a route to an existing section:

```tsx
const routes = [
  {
    label: 'common.routes.application',
    children: [
      {
        label: 'common.routes.dashboard',
        path: pathsConfig.app.home,
        Icon: <Home className={iconClasses} />,
        highlightMatch: `${pathsConfig.app.home}$`,
      },
      {
        label: 'Projects',
        path: '/dashboard/projects',
        Icon: <Folder className={iconClasses} />,
      },
    ],
  },
];
```

### New Section

Add a new collapsible section:

```tsx
const routes = [
  // ... existing sections
  {
    label: 'Your Store',
    children: [
      {
        label: 'Products',
        path: '/dashboard/products',
        Icon: <Package className={iconClasses} />,
      },
      {
        label: 'Orders',
        path: '/dashboard/orders',
        Icon: <ShoppingCart className={iconClasses} />,
      },
    ],
  },
];
```

### Account-aware Routes

Because a single config serves both personal and team accounts, gate any account-specific items at render time using the active account rather than the URL:

```tsx
import { useWorkspace } from '#/components/workspace-context.tsx';

function useNav() {
  const { account } = useWorkspace();

  // e.g. only show a team-only item when a team is active
  return account.is_personal_account ? personalRoutes : teamRoutes;
}
```

## Route Properties

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `label` | `string` | Yes | Display text (supports i18n keys) |
| `path` | `string` | Yes | Route path (static — never encode an account slug) |
| `Icon` | `ReactNode` | No | Lucide icon component |
| `highlightMatch` | `string` | No | Regex pattern for active route highlighting |
| `children` | `Route[]` | No | Nested routes for sub-menus |

## Internationalization

Route labels support i18n keys:

```tsx
{
  label: 'common.routes.dashboard',  // Uses translation
  path: pathsConfig.app.home,
  Icon: <Home className={iconClasses} />,
}
```

Translation files are in `apps/web/i18n/messages/[locale]/common.json`:

```json
{
  "routes": {
    "dashboard": "Dashboard",
    "settings": "Settings",
    "profile": "Profile"
  }
}
```

## Best Practices

1. **Use `pathsConfig`**: Import paths from the configuration instead of hardcoding strings.
2. **Keep paths static**: the active account is DB-resolved — never put an account slug in a path.
3. **Group logically**: Put related routes in the same section.
4. **Consistent icons**: Use icons from `lucide-react` (`w-4`) for visual consistency.

## Common Pitfalls

1. **Missing `highlightMatch`**: the dashboard route needs `highlightMatch: '/dashboard$'` so it does not stay highlighted on every `/dashboard/*` sub-route.
2. **Encoding a slug**: there is no `[account]` placeholder anymore — resolve the active account from `useWorkspace()` instead.
3. **Wrong icon size**: use `w-4` to match the navigation style.

## Related Topics

- [Settings Navigation](/docs/tanstack-supabase/configuration/team-account-sidebar-configuration) - Configure the settings sidebar
- [Paths Configuration](/docs/tanstack-supabase/configuration/paths-configuration) - Centralized path management
