Sidebar

A composable, themeable and customizable sidebar component.

import {
  Sidebar,
  SidebarClose,
  SidebarHeader,
  SidebarInset,
  SidebarOpen,
  SidebarProvider,
  SidebarRail,
} from "@notion-kit/sidebar";
 
export default function Basic() {
  return (
    <SidebarProvider className="h-full min-h-full">
      <Sidebar className="absolute z-0 h-full">
        <SidebarClose />
        <SidebarHeader className="p-3 font-semibold">My App</SidebarHeader>
        <SidebarRail />
      </Sidebar>
      <SidebarInset>
        <header className="flex h-11 shrink-0 items-center gap-2 border-b px-4">
          <SidebarOpen />
          Navbar
        </header>
      </SidebarInset>
    </SidebarProvider>
  );
}
 

Installation

pnpm add @notion-kit/sidebar

Anatomy

<SidebarProvider>
  <Sidebar>
    <SidebarClose />
    <SidebarHeader>{/* Your sidebar header */}</SidebarHeader>
    <SidebarContent>{/* Your sidebar content */}</SidebarContent>
    <SidebarFooter>{/* Your sidebar footer */}</SidebarFooter>
    <SidebarRail />
  </Sidebar>
  <SidebarInset>{/* Your main content */}</SidebarInset>
</SidebarProvider>

Examples


Notion Sidebar

import { Plan, Role } from "@notion-kit/schemas";
import type { Page, User, Workspace } from "@notion-kit/schemas";
import { randomInt } from "@notion-kit/utils";
 
export const GROUPS = {
  document: "Document",
  private: "Private",
  shared: "Shared",
} as const;
 
export const SHORTCUT_OPTIONS = { preventDefault: true };
 
export const user: User = {
  id: "u1",
  name: "Admin",
  email: "admin@email.com",
  avatarUrl: "",
};
 
export const workspaces: Workspace[] = [
  {
    id: "w1",
    name: "Workspace",
    role: Role.OWNER,
    memberCount: 5,
    plan: Plan.FREE,
  },
  {
    id: "w2",
    name: "Workspace 2",
    role: Role.GUEST,
    memberCount: 2,
    plan: Plan.EDUCATION,
  },
  {
    id: "w3",
    name: "Workspace 3",
    role: Role.OWNER,
    memberCount: 10,
    plan: Plan.ENTERPRISE,
  },
  {
    id: "w4",
    name: "Workspace 4",
    role: Role.MEMBER,
    memberCount: 12,
    plan: Plan.PLUS,
  },
];
 
const getRandomTs = () =>
  randomInt(Date.UTC(2024, 1, 1), Date.UTC(2024, 10, 31));
 
export const pages: Page[] = [
  {
    type: "document",
    id: "page1",
    title: "Korean",
    parentId: null,
    icon: {
      type: "url",
      src: "https://img.freepik.com/premium-vector/line-art-flag-language-korean-illustration-vector_490632-422.jpg",
    },
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isArchived: false,
    isFavorite: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page2",
    title: "Pronunciation",
    parentId: "page1",
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page3",
    title: "Study list",
    parentId: null,
    icon: { type: "lucide", src: "book", color: "#CB912F" },
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: true,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page4",
    title: "My secret document",
    icon: { type: "lucide", src: "book-check", color: "#CB912F" },
    parentId: null,
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page5",
    title: "System flowchart",
    parentId: null,
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page6",
    title: "Deprecated documents",
    parentId: null,
    icon: { type: "lucide", src: "book", color: "#337EA9" },
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: true,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "private",
    id: "page7",
    title: "TODO List",
    parentId: null,
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "shared",
    id: "page8",
    title: "System flowchart",
    parentId: null,
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page9",
    parentId: "page3",
    title: "The High Table",
    icon: {
      type: "url",
      src: "https://cdn.iconscout.com/icon/premium/png-256-thumb/bar-table-1447763-1224177.png",
    },
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
  {
    type: "document",
    id: "page10",
    parentId: "page3",
    title: "The Continental",
    icon: { type: "emoji", src: "🏠" },
    lastEditedBy: "",
    lastEditedAt: getRandomTs(),
    isFavorite: false,
    isArchived: false,
    isPublished: false,
    createdAt: Date.UTC(2023, 3, 5),
    createdBy: "",
  },
];
 

API Reference

SidebarProvider

PropTypeDefaultDescription
configPartial<SidebarConfig>-The configuration object of the sidebar.
defaultOpenboolean-The open state of the sidebar when it is initially rendered. Use when you do not need to control its open state.
openboolean-The (controlled) open state of the sidebar. Must be used in conjunction with onOpenChange.
onOpenChange(open: boolean) => void-Handler that is called when the open state of the sidebar changes.

Note

All the layout components below must be used within <SidebarProvider>.

A Sidebar extends the HTML <div> element. We list the extended properties only.

PropTypeDefaultDescription
side"left" | "right""left"The side of the sidebar.
variant"sidebar" | "floating""sidebar"The variant of the sidebar.
collapsible"offcanvas" | "none""offcanvas"Collapsible state of the sidebar.

SidebarHeader

A SidebarHeader extends the HTML <div> element.

SidebarContent

A SidebarContent extends the HTML <div> element.

SidebarFooter

A SidebarFooter extends the HTML <div> element.

SidebarGroup

A SidebarGroup extends the HTML <div> element.

SidebarInset

A SidebarInset extends the HTML <main> element.

SidebarRail

The SidebarRail is used to render a rail within a <Sidebar>. This rail can be used to toggle and resize the sidebar. It extends the HTML <div> element. We list the extended properties only.

PropTypeDefaultDescription
enableDragbooleantrueWhether the sidebar is draggable.

SidebarClose

The SidebarClose is used to close the sidebar. It can be triggered by keyboard shortcut. It extends the <Button> component.

SidebarOpen

The SidebarOpen is used to open the sidebar. It can be triggered by keyboard shortcut. It extends the <Button> component.

SidebarMenuItem

A SidebarMenuItem extends the HTML <div> element. We list the extended properties only.

PropTypeDefaultDescription
label*string-The title of the item.
hint*string-The tooltip of the item.
icon*LucideIcon-The displayed icon of the item.
shortcutstring-The keyboard shortcut of the item.

type SidebarConfig

The configuration object of the sidebar.

PropTypeDefaultDescription
defaultWidth*string248pxDefault width of the sidebar.
defaultModileWidth*string248pxDefault width of the sidebar on mobile.
minWidth*string248pxMinimum draggable width of the sidebar.
maxWidth*string400pxMaximum draggable width of the sidebar.
shortcut*string\Keyboard shortcut ⌘ + <shortcut> to toggle the sidebar.
cookieName*{ state: string, width: string }{ state: "sidebar:state", width: "sidebar:width" }Cookie names for sidebar state and sidebar width.
cookieMaxAge*number7dCookie's maximum lifetime (in seconds).

On this page