Slots Configuration

Inject custom content at predefined positions through slots

Type Definition

interface SlotsConfig {
  /**
   * Title bar slots
   */
  titleBar?: {
    left?: ReactNode;
    center?: ReactNode;
    right?: ReactNode;
  };

  /**
   * Sidebar slots
   */
  sidebar?: {
    header?: ReactNode;
    footer?: ReactNode;
    beforeNav?: ReactNode;
    afterNav?: ReactNode;
  };

  /**
   * Shell slots
   */
  shell?: {
    beforeContent?: ReactNode;
    afterContent?: ReactNode;
  };
}

Title Bar Slots

Inject content at different positions in the title bar:

┌─────────────────────────────────────────────────┐
│  [left]          [center]          [right] [×]  │
└─────────────────────────────────────────────────┘
slots: {
  titleBar: {
    left: <SearchInput />,
    center: <BreadcrumbNav />,
    right: <UserMenu />,
  },
},
function SearchInput() {
  return (
    <input
      type="search"
      placeholder="Search..."
      className="h-7 w-48 rounded border px-2 text-sm"
    />
  );
}

slots: {
  titleBar: {
    left: <SearchInput />,
  },
},

Inject content at different positions in the sidebar:

┌────────────┐
│  [header]  │
├────────────┤
│[beforeNav] │
│            │
│  Nav Items │
│            │
│ [afterNav] │
├────────────┤
│  [footer]  │
└────────────┘
slots: {
  sidebar: {
    header: <WorkspaceSwitcher />,
    beforeNav: <QuickActions />,
    afterNav: <StorageIndicator />,
    footer: <UserProfile />,
  },
},

Example: Adding User Info

function UserProfile() {
  return (
    <div className="flex items-center gap-2 p-3 border-t">
      <img src="/avatar.png" className="w-8 h-8 rounded-full" />
      <div>
        <div className="text-sm font-medium">John Doe</div>
        <div className="text-xs text-muted-foreground">john@example.com</div>
      </div>
    </div>
  );
}

slots: {
  sidebar: {
    footer: <UserProfile />,
  },
},

Example: Adding Workspace Switcher

function WorkspaceSwitcher() {
  return (
    <select className="w-full p-2 border-b text-sm">
      <option>Personal Workspace</option>
      <option>Team Workspace</option>
    </select>
  );
}

slots: {
  sidebar: {
    header: <WorkspaceSwitcher />,
  },
},

Shell Slots

Inject content before and after the main content area:

┌────────────────────────────────────────┐
│            TitleBar                     │
├────────────┬───────────────────────────┤
│            │  [beforeContent]          │
│  Sidebar   ├───────────────────────────┤
│            │     Main Content          │
│            ├───────────────────────────┤
│            │  [afterContent]           │
└────────────┴───────────────────────────┘
slots: {
  shell: {
    beforeContent: <AlertBanner />,
    afterContent: <StatusBar />,
  },
},

Example: Adding Alert Banner

function AlertBanner() {
  return (
    <div className="bg-yellow-100 border-b border-yellow-200 px-4 py-2 text-sm text-yellow-800">
      System maintenance scheduled for 10:00 PM tonight
    </div>
  );
}

slots: {
  shell: {
    beforeContent: <AlertBanner />,
  },
},

Example: Adding Status Bar

function StatusBar() {
  return (
    <div className="border-t px-4 py-1 text-xs text-muted-foreground flex justify-between">
      <span>Connected</span>
      <span>Last sync: 5 minutes ago</span>
    </div>
  );
}

slots: {
  shell: {
    afterContent: <StatusBar />,
  },
},

Complete Example

import { WorkspaceSwitcher } from './components/WorkspaceSwitcher';
import { UserProfile } from './components/UserProfile';
import { SearchInput } from './components/SearchInput';
import { StatusBar } from './components/StatusBar';

export const config = {
  slots: {
    titleBar: {
      left: <SearchInput />,
    },
    sidebar: {
      header: <WorkspaceSwitcher />,
      footer: <UserProfile />,
    },
    shell: {
      afterContent: <StatusBar />,
    },
  },
};

Notes

Slot content is static and defined at configuration time. If you need dynamic content, manage state inside the slot component.

// Correct: manage state inside the component
function DynamicCounter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

slots: {
  sidebar: {
    footer: <DynamicCounter />,
  },
},