App Layout

v0.2.0

Full page shell: header, collapsible desktop sidebar, and sheet-based mobile drawer composed around your page content.

View as Markdown

Install

npx shadcn@latest add @signalos/app-layout

Requires a configured @signalos registry and a valid SIGNALOS_REGISTRY_TOKEN - get access. Registry dependencies (@signalos/tokens, @signalos/utils) are pulled automatically.

Preview

Example & code

app-layout.example.tsx
import { useState } from "react"
import { Inbox, LayoutDashboard, Settings } from "lucide-react"

import { AppLayout } from "@/components/widgets/app-layout/AppLayout"
import type { AppLayoutNavItem } from "@/components/widgets/app-layout/AppLayout.types"

const navigationItems: AppLayoutNavItem[] = [
  {
    id: "dashboard",
    label: "Dashboard",
    collapsedLabel: "Home",
    href: "#/dashboard",
    icon: LayoutDashboard,
    active: true,
  },
  {
    id: "feed",
    label: "Signal Feed",
    collapsedLabel: "Feed",
    href: "#/feed",
    icon: Inbox,
  },
  {
    id: "settings",
    label: "Settings",
    href: "#/settings",
    icon: Settings,
    section: "bottom",
  },
]

export default function Example() {
  const [collapsed, setCollapsed] = useState(false)

  return (
    <AppLayout
      navigationItems={navigationItems}
      logo={
        <span className="text-lg font-semibold text-sidebar-foreground">
          Acme Ops
        </span>
      }
      header={<span className="text-sm font-medium">Dashboard</span>}
      isSidebarCollapsed={collapsed}
      onSidebarCollapsedChange={setCollapsed}
    >
      <p className="text-sm text-muted-foreground">
        Route content renders here.
      </p>
    </AppLayout>
  )
}

Props

AppLayoutNavItem

PropTypeDefaultDescription
id*string-
label*string-
href*string-
iconComponentType<{ className?: string | undefined; }> | undefined-
section"main" | "bottom" | undefined-
activeboolean | undefined-
collapsedLabelstring | undefined-

AppLayoutLinkComponentProps

PropTypeDefaultDescription
href*string-
classNamestring | undefined-
onClick(() => void) | undefined-
aria-current"page" | undefined-
data-testidstring | undefined-
childrenReactNode-

AppLayoutProps

PropTypeDefaultDescription
navigationItems*AppLayoutNavItem[]-
children*ReactNode-
logoReactNode-
headerReactNode-
isSidebarCollapsedboolean | undefined-
onSidebarCollapsedChange((collapsed: boolean) => void) | undefined-
isMobileNavOpenboolean | undefined-
onMobileNavOpenChange((open: boolean) => void) | undefined-
onNavigate((item: AppLayoutNavItem) => void) | undefined-
LinkComponentComponentType<AppLayoutLinkComponentProps> | undefined-Component used to render navigation links, e.g. Next.js `Link` or a router-aware anchor. Defaults to a plain `<a>` tag (full page reload).
classNamestring | undefined-
data-testidstring | undefined-Test identifier rendered as `data-testid` on the root element.

npm dependencies

lucide-react@^1.7.0

Changelog

app-layout

0.2.0

  • Breaking: rename the testId prop to data-testid to match the rest of the registry and the DOM attribute name.

0.1.2

  • Removed the next/link dependency from sidebar/mobile navigation. Added an optional LinkComponent prop so host apps can supply their own router-aware link (e.g. Next.js Link); defaults to a plain <a> tag when not provided.
  • Content sync fix to bring source in line with published registry payload.

0.1.0

  • Initial release: full shell composition (header + collapsible sidebar + sheet-style mobile drawer + main content area) around a single <AppLayout> component. Migrated and renamed from the app-shell2 scaffold; internal Header/Sidebar/MobileNav/ NavList pieces were inlined into AppLayout.tsx to match the registry's flat per-item file contract.