Layout
layout把页面拆成页头、侧栏、内容和页脚四块,侧栏可折叠
用法
经典中后台外壳
左侧 Sider + 右侧 Header / Content / Footer,含侧栏时自动横向布局。
页面内容
第 1 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 2 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 3 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 4 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 5 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 6 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 7 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 8 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
const [collapsed, setCollapsed] = useState(false)
<Layout className="h-full" hasSider>
<Layout.Sider collapsible collapsed={collapsed} onCollapse={setCollapsed}>
<div className={cn(
"flex h-[var(--hl-layout-header-h)] items-center font-semibold",
collapsed ? "justify-center" : "px-4",
)}>
{collapsed ? "瑚" : "瑚琏控制台"}
</div>
<NavMenu items={items} mode={collapsed ? "collapsed" : "inline"} selectedKeys={["dashboard"]} />
</Layout.Sider>
<Layout>
<Layout.Header sticky>
<span className="font-medium">中后台外壳</span>
<span className="ml-auto text-sm text-muted-foreground">user@hulian</span>
</Layout.Header>
<Layout.Content>{/* 页面内容 */}</Layout.Content>
<Layout.Footer>瑚琏 Layout · © 2026</Layout.Footer>
</Layout>
</Layout>通栏头部在上
Header 横跨全宽,下方再分 Sider + Content,最外层为纵向布局。
页面内容
第 1 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 2 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 3 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 4 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 5 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 6 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 7 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 8 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
<Layout className="h-full">
<Layout.Header>
<span className="font-medium">通栏头部</span>
</Layout.Header>
<Layout>
<Layout.Sider collapsible>
<NavMenu items={items} />
</Layout.Sider>
<Layout.Content>{/* 页面内容 */}</Layout.Content>
</Layout>
<Layout.Footer>底部通栏 · © 2026</Layout.Footer>
</Layout>默认收起侧栏
defaultCollapsed 让侧栏初始收起,NavMenu 切到 collapsed 图标轨。
页面内容
第 1 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 2 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 3 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 4 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 5 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 6 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 7 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 8 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
<Layout.Sider collapsible defaultCollapsed>
<NavMenu items={items} mode="collapsed" />
</Layout.Sider>何时用
需要 Header/Sider/Content/Footer 这种通用页面骨架、且自己掌控菜单与页签逻辑时用 Layout(更原子、更自由)。若要开箱即用的中后台外壳(品牌 + NavMenu + 多页签 keep-alive 已内置),直接用 AdminLayout,它就是基于本组件的成品装配。
导入
import { Layout, LayoutHeader, LayoutSider, LayoutContent, LayoutFooter } from "@hulianui/ui"Props
Layout(也可用 Layout.Sider 等点访问)
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| hasSider | boolean | 自动探测 | 强制横向(含侧栏)布局。缺省按直接子元素是否含 Layout.Sider 判定:含则横向 row,否则纵向 col。异步/条件渲染 Sider 时用它兜底。 |
继承 HTMLAttributes<HTMLDivElement>。
LayoutHeader
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| sticky | boolean | false | 吸顶(sticky top-0)。 |
LayoutSider
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| width | number | 240 | 展开态宽度(px)。 |
| collapsedWidth | number | 64 | 收起态宽度(px),留出 icon-only 菜单。 |
| collapsible | boolean | false | 是否可折叠(显示底部 trigger 折叠按钮)。 |
| collapsed | boolean | - | 受控收起态。传入即受控,须配合 onCollapse 回写。 |
| defaultCollapsed | boolean | false | 非受控初始收起态。 |
| breakpoint | "sm" | "md" | "lg" | "xl" | "2xl" | number | - | 响应式断点:视口 ≤ 该宽度自动收起,> 时展开。 |
LayoutContent / LayoutFooter
纯 HTMLAttributes<HTMLElement>,无专属 prop。
Events
LayoutSider
| 事件 | 类型 | 说明 |
|---|---|---|
| onCollapse | (collapsed: boolean, type: "clickTrigger" | "responsive") => void | 收起态变化回调(点 trigger 或命中断点时触发)。 |
Slots
LayoutSider
| 插槽 | 类型 | 说明 |
|---|---|---|
| trigger | ReactNode | 底部折叠触发器内容。undefined=默认 chevron;null=不渲染触发器(即便 collapsible);传节点=自定义触发器内容(仍由本件包裹为可点按钮)。 |
禁忌 / 坑
- `Layout.Sider` 只负责宽度,不负责内容。折叠时品牌区显示全称还是单字、
NavMenu走inline还是collapsed,都得消费方按折叠态自己切——所以collapsed要自管 state(受控传给 Sider +onCollapse回写),不能只喂一个defaultCollapsed初始值就完事。只喂初始值的话,用户点折叠后侧栏宽度确实收到 64px,内容却停在展开态,5 个中文字缩到 min-content 会一字一行竖排并把顶部撑高(#120)。折叠态也可以直接读 Sider 根节点上的data-collapsed写 CSS。 - 顶栏高度读 `--hl-layout-header-h`(4rem)。要做「侧栏顶部 logo 区与 Header 齐平」这件几乎必做的事,两边用
h-[var(--hl-layout-header-h)]即可,别去源码里翻那个数——翻错一次就是一条永远对不齐的分隔线。
- 受控/非受控二选一:
Layout.Sider传了collapsed即进入受控,必须配onCollapse回写,否则 trigger/断点点了不动;只想非受控用defaultCollapsed。 - 横/纵布局靠子元素探测:嵌套子
Layout是否横排取决于其直接子是否含Layout.Sider。Sider 异步/条件渲染、或被包装进自定义组件(如<AppSider/>内部才渲染Layout.Sider)时探测会落空,须显式给hasSider。 - 收起态切窄宽用菜单的
mode="collapsed"(dogfood NavMenu 自带 icon-only 轨),不要手搓「窄宽裁切 label」。
相关
AdminLayout · ScrollArea · Viewport · Resizable · AspectRatio · FitScreen
Playground
页面内容
第 1 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 2 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 3 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 4 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 5 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 6 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 7 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
第 8 段占位内容 —— Content 区 flex-auto 占满并独立滚动,Header 可吸顶、Footer 沉底。
<Layout>
<Layout.Sider collapsible>
{/* dogfood NavMenu:收起态自动切图标轨 */}
<NavMenu items={items} mode={"inline"} />
</Layout.Sider>
<Layout>
<Layout.Header sticky>顶栏</Layout.Header>
<Layout.Content>内容</Layout.Content>
<Layout.Footer>底栏</Layout.Footer>
</Layout>
</Layout>