---
title: Customizing Systems
description: One pattern for customizing system sidebars and pages -- set manifest.sidebar to a component composing the system's published pieces. Decision tree + three worked examples.
---
<!-- @generated by packages/sdk/scripts/copy-reference-docs.mjs -- DO NOT EDIT -->
<!-- Regenerate: pnpm scaffold:sync -->


# Customizing Systems

**Status:** 🟢 Stable

> **Requires `@elevasis/ui` >= 2.8.1.**

There is one pattern for customizing a system's sidebar or pages: set `sidebar` on your manifest override to a component that composes the system's published pieces. Pass custom `items` to swap the nav array, or compose `SidebarTop` + `SidebarMiddle` + exported panels directly for structural changes. Never fork a source file as a first resort -- if you copy source you own upstream drift forever.

## Decision Tree

**Adding or changing nav items?** Extend the exported item array and pass to `<*SidebarMiddle items={...}>`. This covers the vast majority of sidebar customization needs.

**Structural changes (injecting panels, reordering sections, wrapping sections)?** Compose `SidebarTop`, `SidebarMiddle`, and exported panels (`MyTasksPanel`, `QuickCreateActions`) directly into your own component and assign it to the manifest's `sidebar` field.

**Wrapping pages?** Import the system's page component and wrap with custom chrome in a route file.

**Neither fits?** The missing export is a bug in `@elevasis/ui` -- file an issue. Copying source is physically possible but unsupported. You own upstream drift forever.

## Worked Example 1: Sidebar Nav Extension

Extend CRM's nav by spreading `CRM_ITEMS` and appending a Reports entry. Pass the extended array to `CrmSidebarMiddle`.

Wire the override in `ui/src/routes/__root.tsx`, where system manifests are assembled before being passed to `ElevasisSystemsProvider`.

```tsx
// ui/src/routes/__root.tsx (excerpt -- add after existing imports)

import { crmManifest, CrmSidebar, CrmSidebarMiddle, CRM_ITEMS } from '@elevasis/ui/features/crm'
import { IconFileText } from '@tabler/icons-react'
import type { NavItem } from '@elevasis/ui/layout'

// Extend the published CRM nav array with a project-specific Reports entry.
const customCrmItems: NavItem[] = [
  ...CRM_ITEMS,
  { label: 'Reports', to: '/crm/reports', icon: IconFileText, exact: false }
]

// Compose CrmSidebar (Top + Middle) with the extended item list.
const MyCrmSidebar = () => (
  <CrmSidebar>
    <CrmSidebarMiddle items={customCrmItems} />
  </CrmSidebar>
)

// Override the manifest's sidebar, keeping every other field unchanged.
const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
```

Then swap `crmManifest` for `customCrmManifest` in the `SYSTEM_MANIFESTS` array:

```tsx
// In the SYSTEM_MANIFESTS array (same file)
const SYSTEM_MANIFESTS: SystemModule[] = [
  leadGenManifest,
  customCrmManifest,   // <-- replaces crmManifest
  deliveryManifest,
  operationsManifest,
  monitoringManifest,
  settingsManifest
]
```

Your new `/crm/reports` route file still needs to be created in `ui/src/routes/crm/` -- the nav item just controls the link.

## Worked Example 2: Sidebar with Injected Panel

Inject a custom panel between the nav list and the My Tasks section. Compose the parts explicitly rather than relying on `CrmSidebarMiddle`'s default layout.

```tsx
// ui/src/routes/__root.tsx (excerpt)

import {
  crmManifest,
  CrmSidebar,
  CrmSidebarMiddle,
  CrmSidebarTop,
  MyTasksPanel,
  QuickCreateActions,
  CRM_ITEMS
} from '@elevasis/ui/features/crm'
import { SubshellNavList, SubshellSidebarSection } from '@elevasis/ui/layout'
import { Stack } from '@mantine/core'
import { IconChartBar } from '@tabler/icons-react'

// Custom middle that inserts a Pipeline Health panel between nav and My Tasks.
const MyCrmMiddle = () => (
  <Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
    <Stack gap={0} p="sm">
      <SubshellNavList items={CRM_ITEMS} />
    </Stack>
    {/* Inject custom panel here */}
    <SubshellSidebarSection icon={IconChartBar} label="Pipeline Health" withTopBorder />
    <Stack gap={0} p="sm">
      <PipelineHealthWidget />
    </Stack>
    {/* Preserve the My Tasks section from the default middle */}
    <MyTasksPanel footer={<QuickCreateActions showSectionLabel={false} />} showSectionLabel />
  </Stack>
)

// Full sidebar: Top stays unchanged, Middle is replaced.
const MyCrmSidebar = () => (
  <CrmSidebar>
    <MyCrmMiddle />
  </CrmSidebar>
)

const customCrmManifest = { ...crmManifest, sidebar: MyCrmSidebar }
```

`PipelineHealthWidget` is a component you define in `ui/src/features/crm/` or `ui/src/lib/components/`. Keep route files and `__root.tsx` thin -- push component logic into feature modules.

## Worked Example 3: Page Wrapping

Import a system's page component and wrap it with custom chrome in a route file. This keeps the system's business logic intact while adding project-specific framing (banners, secondary nav, analytics context, etc.).

The exported CRM page components are `DealsListPage` and `DealDetailPage`:

```tsx
// ui/src/routes/crm/deals.index.tsx

import { createFileRoute } from '@tanstack/react-router'
import { DealsListPage } from '@elevasis/ui/features/crm'
import { ProtectedRoute } from '@/features/auth'
import { ProjectAnnouncementBanner } from '@/lib/components/ProjectAnnouncementBanner'
import { Stack } from '@mantine/core'

export const Route = createFileRoute('/crm/deals/')({
  component: DealsListPageGuarded
})

function DealsListPageGuarded() {
  return (
    <ProtectedRoute>
      <Stack gap={0}>
        <ProjectAnnouncementBanner context="crm-deals" />
        <DealsListPage />
      </Stack>
    </ProtectedRoute>
  )
}
```

The pattern is identical for `DealDetailPage`. Import the published component, wrap it in your chrome, export as the route component. No forking, no copying source.

## Delivery: Three-Section Sidebar

Delivery keeps three named sections (Projects, Work, Communication). It exports three separate arrays and `ProjectsSidebarMiddle` accepts three matching optional props -- one per section. There is no single `items` prop.

```tsx
// Adding items to an existing delivery section
import {
  deliveryManifest,
  ProjectsSidebar,
  ProjectsSidebarMiddle,
  DELIVERY_PROJECT_ITEMS,
  DELIVERY_WORK_ITEMS,
  DELIVERY_COMMUNICATION_ITEMS
} from '@elevasis/ui/features/delivery'
import type { NavItem } from '@elevasis/ui/layout'
import { IconChartBar } from '@tabler/icons-react'

const customProjectItems: NavItem[] = [
  ...DELIVERY_PROJECT_ITEMS,
  { label: 'Analytics', to: '/projects/analytics', icon: IconChartBar, exact: false }
]

const MyDeliverySidebar = () => (
  <ProjectsSidebar>
    <ProjectsSidebarMiddle
      projectItems={customProjectItems}
      workItems={DELIVERY_WORK_ITEMS}
      communicationItems={DELIVERY_COMMUNICATION_ITEMS}
    />
  </ProjectsSidebar>
)

const customDeliveryManifest = { ...deliveryManifest, sidebar: MyDeliverySidebar }
```

**Adding a new section** (not extending an existing one) requires composing from primitives. Use `SubshellSidebarSection` as the section header and `SubshellNavList` to render items inside it:

```tsx
import {
  ProjectsSidebar,
  DELIVERY_PROJECT_ITEMS,
  DELIVERY_WORK_ITEMS,
  DELIVERY_COMMUNICATION_ITEMS
} from '@elevasis/ui/features/delivery'
import { SubshellSidebarSection, SubshellNavList } from '@elevasis/ui/layout'
import { Stack } from '@mantine/core'
import { IconChartBar, IconListCheck, IconMessageCircle, IconBriefcase } from '@tabler/icons-react'
import type { NavItem } from '@elevasis/ui/layout'

const ANALYTICS_ITEMS: NavItem[] = [
  { label: 'Dashboard', to: '/projects/analytics', icon: IconChartBar, exact: true }
]

const MyDeliveryMiddle = () => (
  <Stack gap={0} style={{ flex: 1, overflowY: 'auto' }}>
    <Stack gap={0} p="sm">
      <SubshellNavList items={DELIVERY_PROJECT_ITEMS} />
    </Stack>
    <SubshellSidebarSection icon={IconListCheck} label="Work" withTopBorder />
    <Stack gap={0} p="sm">
      <SubshellNavList items={DELIVERY_WORK_ITEMS} />
    </Stack>
    <SubshellSidebarSection icon={IconMessageCircle} label="Communication" withTopBorder />
    <Stack gap={0} p="sm">
      <SubshellNavList items={DELIVERY_COMMUNICATION_ITEMS} />
    </Stack>
    <SubshellSidebarSection icon={IconChartBar} label="Analytics" withTopBorder />
    <Stack gap={0} p="sm">
      <SubshellNavList items={ANALYTICS_ITEMS} />
    </Stack>
  </Stack>
)

const MyDeliverySidebar = () => <ProjectsSidebar><MyDeliveryMiddle /></ProjectsSidebar>
```

This is the same compose-from-primitives path CRM consumers use for structural changes. No special API -- just `SubshellSidebarSection` + `SubshellNavList` from `@elevasis/ui/layout`.

For broader CRM extension work across pages, hooks, actions, workflows, and org-model boundaries, start with `node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-crm.md`.

For broader lead-gen extension work across pages, hooks, list/member state, artifacts, workflows, and org-model boundaries, start with `node_modules/@elevasis/sdk/reference/scaffold/recipes/extend-lead-gen.md`.

## Imports Cheat Sheet

| What you need                                                                                                                                                         | Import from                      |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `CRM_ITEMS`, `CrmSidebar`, `CrmSidebarTop`, `CrmSidebarMiddle`, `MyTasksPanel`, `QuickCreateActions`, `DealsListPage`, `DealDetailPage`, `crmManifest`                | `@elevasis/ui/features/crm`      |
| `LEAD_GEN_ITEMS`, `LeadGenSidebar`, `LeadGenSidebarTop`, `LeadGenSidebarMiddle`, `leadGenManifest`                                                                    | `@elevasis/ui/features/lead-gen` |
| `DELIVERY_PROJECT_ITEMS`, `DELIVERY_WORK_ITEMS`, `DELIVERY_COMMUNICATION_ITEMS`, `ProjectsSidebar`, `ProjectsSidebarTop`, `ProjectsSidebarMiddle`, `deliveryManifest` | `@elevasis/ui/features/delivery` |
| `SubshellNavList`, `SubshellNavItem`, `SubshellSidebarSection`, `NavItem`                                                                                             | `@elevasis/ui/layout`            |
| `SystemModule`                                                                                                                                                        | `@elevasis/ui/provider`          |

Operations, monitoring, and settings have no customizable sidebar items. Operations sidebar is route-aware dispatch and not extended via the `items` pattern.
