用 tldraw SDK 隐藏内置 UI:TLUiComponents 置空与 hideUi 深度指南
本文围绕 tldraw SDK 官方示例
ui-components-hidden展开:tldraw 把默认界面拆解为TLUiComponents中的一个个“插槽(slot)”,将任一插槽设为null即可精确移除对应的菜单、面板或工具条;若想一次性去掉整套默认 UI、只保留画布与编辑器能力,则用hideUiprop。读完你将掌握「按需裁剪」与「完全无头(headless)」两种 UI 隐藏方案,并能在此基础上用自己的 React 组件替换被隐藏的部分。
背景:tldraw 默认 UI 的分层架构
tldraw 编辑器(<Tldraw>)由编辑内核(canvas、shapes、tools、store)与默认 UI 外壳(工具栏、菜单、面板、对话框等)组成。默认 UI 并非铁板一块,而是全部注册在一个中央配置对象中。
从源码看,这个配置对象定义于 packages/tldraw/src/lib/ui/context/components.tsx 的 TLUiComponents 接口:
/** @public */
export interface TLUiComponents {
ContextMenu?: ComponentType<TLUiContextMenuProps> | null
ActionsMenu?: ComponentType<TLUiActionsMenuProps> | null
HelpMenu?: ComponentType<TLUiHelpMenuProps> | null
ZoomMenu?: ComponentType<TLUiZoomMenuProps> | null
MainMenu?: ComponentType<TLUiMainMenuProps> | null
Minimap?: ComponentType | null
StylePanel?: ComponentType<TLUiStylePanelProps> | null
PageMenu?: ComponentType | null
NavigationPanel?: ComponentType | null
Toolbar?: ComponentType | null
RichTextToolbar?: ComponentType<TLUiRichTextToolbarProps> | null
ImageToolbar?: ComponentType | null
VideoToolbar?: ComponentType | null
KeyboardShortcutsDialog?: ComponentType<TLUiKeyboardShortcutsDialogProps> | null
QuickActions?: ComponentType<TLUiQuickActionsProps> | null
HelperButtons?: ComponentType<TLUiHelperButtonsProps> | null
DebugPanel?: ComponentType | null
DebugMenu?: ComponentType | null
MenuPanel?: ComponentType | null
TopPanel?: ComponentType | null
SharePanel?: ComponentType | null
CursorChatBubble?: ComponentType | null
Dialogs?: ComponentType | null
Toasts?: ComponentType | null
A11y?: ComponentType | null
FollowingIndicator?: ComponentType | null
PeopleMenu?: ComponentType | null
PeopleMenuAvatar?: ComponentType<TLUiPeopleMenuAvatarProps> | null
PeopleMenuItem?: ComponentType<TLUiPeopleMenuItemProps> | null
PeopleMenuFacePile?: ComponentType<TLUiPeopleMenuFacePileProps> | null
UserPresenceEditor?: ComponentType | null
}
这些字段的值类型是 ComponentType<...> | null:每一项都可以被替换成自定义 React 组件,或被赋值为 null 从界面中整体移除。二者正是 tldraw UI 定制的一体两面——本文聚焦“移除(置 null)”这一面,而“替换”能力意味着你随时可在原位置放入自己的实现。
TldrawUiComponentsProvider(同一文件)为每个插槽提供了默认实现(DefaultToolbar、DefaultContextMenu、DefaultToasts……),随后用 ..._overrides 展开覆盖。你传入 components 的对象就是这份 override:命中的键覆盖默认实现或置空,未命中的键保留默认 UI。
此外,这些 UI 插槽与编辑内核的组件(如 Scribble、Canvas 等 TLEditorComponents)共同组成了 <Tldraw> 上的 components prop,完整定义见 packages/tldraw/src/lib/Tldraw.tsx 的 TLComponents extends TLEditorComponents, TLUiComponents {}。
精确隐藏:将插槽置为 null
tldraw 官方示例 ui-components-hidden/README.md 的结论非常直接:
Hide any built-in UI component by setting its slot to
null.
也就是说,默认 UI 的每一部分都是一个插槽,给该插槽传 null 即可将它从界面上移除。
完整示例:一口气隐藏全部内置 UI
示例实现见 UiComponentsHiddenExample.tsx。它使用 Required<TLUiComponents> 把每个插槽都显式置为 null,其价值首先是一份权威的“全部插槽清单”参考:
import { TLUiComponents, Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'
// [1]
const components: Required<TLUiComponents> = {
ContextMenu: null,
ActionsMenu: null,
HelpMenu: null,
ZoomMenu: null,
MainMenu: null,
Minimap: null,
StylePanel: null,
PageMenu: null,
NavigationPanel: null,
Toolbar: null,
KeyboardShortcutsDialog: null,
QuickActions: null,
HelperButtons: null,
DebugPanel: null,
DebugMenu: null,
SharePanel: null,
MenuPanel: null,
TopPanel: null,
CursorChatBubble: null,
RichTextToolbar: null,
ImageToolbar: null,
VideoToolbar: null,
Dialogs: null,
Toasts: null,
A11y: null,
FollowingIndicator: null,
PeopleMenu: null,
PeopleMenuAvatar: null,
PeopleMenuItem: null,
PeopleMenuFacePile: null,
UserPresenceEditor: null,
}
export default function UiComponentsHiddenExample() {
return (
<div className="tldraw__editor">
<Tldraw components={components} />
</div>
)
}
示例注释特别强调:
Setting a slot to
nullremoves that part of the UI. Set only the slots you want to hide; theRequired<>type is here so this example lists every slot, which is handy as a reference.
即示例代码刻意用 Required<> 列出全部插槽,是为了充当参考索引;实际业务中你只需要置空真正想去掉的那几个插槽,不要照抄全量清单。
按槽位类型逐一解读
结合 components.tsx 的类型定义,这 31 个插槽可归为几类,便于按需选择:
| 类别 | 插槽 | 说明 |
|---|---|---|
| 顶部菜单区 | MenuPanel、MainMenu、HelpMenu、ZoomMenu、ActionsMenu |
左上主菜单、帮助、缩放菜单与顶部动作菜单,MenuPanel 是装载它们的顶栏容器 |
| 核心画布工具 | Toolbar、QuickActions、HelperButtons、StylePanel、RichTextToolbar、ImageToolbar、VideoToolbar |
左侧工具条、快捷操作、辅助按钮、右侧样式面板,以及选中文本/图片/视频时浮出的上下文工具条 |
| 导航与页面 | Minimap、NavigationPanel、PageMenu、ContextMenu |
右下小地图、页面导航面板、页面菜单与画布右键菜单 |
| 多人在线(协作) | SharePanel、PeopleMenu、PeopleMenuAvatar、PeopleMenuItem、PeopleMenuFacePile、CursorChatBubble、FollowingIndicator、UserPresenceEditor |
分享面板、协作者头像菜单/头像列表/人脸堆叠、光标聊天气泡、跟随指示器与用户在场编辑。注意默认情况下 SharePanel、CursorChatBubble 仅在协作 UI 开启时才显示(见 components.tsx 中 showCollaborationUi ? DefaultSharePanel : null 的写法) |
| 弹层与对话框 | Dialogs、Toasts、KeyboardShortcutsDialog、DebugMenu、DebugPanel |
对话框宿主、Toast 宿主、快捷键对话框、调试菜单与底部调试面板 |
| 其他 | TopPanel、A11y |
顶部通栏与无障碍播报。TopPanel 默认即为 null,只有需要顶部区域时才自定义 |
隐藏弹层/Toast/无障碍系统时的连带效应
README 给出一个重要警告:
Hiding
Toasts,Dialogs, orA11yalso disables the features that render through them.
这三者不是“纯外观”插槽,而是功能管道:
- 把
Toasts置为null:Toast 宿主不再挂载,编辑器内部通过 toast 系统发出的通知将无处渲染,相关用户提示全部不可见; - 把
Dialogs置为null:所有对话框失去宿主,典型的如「按?打开的键盘快捷键面板」等弹窗流程都会随之失效; - 把
A11y置为null:默认的无障碍(screen reader)播报器被移除,编辑器的实时状态播报关闭。
所以在定制时务必判断:你是只想“看不见”,还是连“背后的功能通路”一起关掉。如果只是想移走视觉元素而保留底层交互,应优先考虑把相关入口(菜单项、按钮)一并从 UI 中删除,而不是直接干掉 Toasts/Dialogs 宿主——官方 README 的表述明确提示了这两者的差异。
一键隐藏全部 UI:hideUi prop
如果你想要一个“只剩画布”的精简编辑器——保留画布、默认图形、全部工具与快捷键,但完全不渲染任何内置 UI——tldraw 提供了比逐项置空更直接的方式:<Tldraw hideUi />。
配套示例见 hide-ui/README.md 与 HideUiExample.tsx:
import { Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'
export default function HideUiExample() {
return (
<div className="tldraw__editor">
<Tldraw persistenceKey="hide-ui-example" hideUi />
</div>
)
}
其底层实现位于 packages/tldraw/src/lib/ui/TldrawUi.tsx:hideUi 是 UI 组件的布尔属性,组件内部通过条件渲染阻止整个 UI 内容挂载。同时 Tldraw 组件在组合“画布之上”的图层时也做了联动判断——见 Tldraw.tsx:当 hideUi 为真时,不会再包一层默认 UI 外壳。换言之,hideUi 不是“把 31 个槽位逐个置空”,而是从 UI 渲染树的根部整体跳过,彻底性优于手动清空清单。
与 components 置空的差异点值得记住:
components逐项置null:适合保留 UI 整体结构、只去掉个别部件;Toasts/Dialogs/A11y被置空时还会顺带关闭背后的功能管道;hideUi:适合完全无内置 UI 的嵌入场景(编辑器内核 + 你自己写的整套控制层)。行为正如官方注释所述——“removes all of tldraw's default UI but keeps the editor, its tools, and its keyboard shortcuts”。
实践建议:隐藏之后如何提供替代控制
无论用哪种方式,隐藏 UI 都不会禁用编辑器能力:
- 快捷键仍然有效:
hide-ui示例提示可以按d切换到画笔(draw)、按r插入矩形,因为工具切换走的是键盘快捷键系统,与 UI 渲染解耦; - 自建控制面板:在
<Tldraw>内部渲染自组件,用useEditor()hook 获取编辑器实例并调用editor.setCurrentTool('draw')、editor.getSelectedShapes()等方法即可搭建自己的工具条/属性面板。官方推荐的起点是 custom-ui 示例(hide-ui 的 README 中也明确指引 “See the custom UI example for building your own controls”); - 按产品形态组合:需要“原生手感但去掉小地图/分享菜单”的,用
components精准置空个别槽位;要做“白板内核 + 完全自定义外壳”(如笔记应用、设计工具内嵌画布)的,用hideUi+useEditor()自建控制层。
小结
从官方示例 ui-components-hidden 出发,tldraw 的 UI 隐藏体系可以总结为两条互补路径:
- 精确裁剪:把
TLUiComponents中的任意插槽设为null,按需移除单个 UI 部件;Required<TLUiComponents>全量置空清单可作为插槽名速查表;同时留意Toasts、Dialogs、A11y三个宿主插槽被移除时的功能连带关闭。 - 整体隐藏:传
hideUiprop,从渲染根部跳过整个默认 UI 外壳,得到保留画布、工具与快捷键的“裸”编辑器,再用useEditor()打造自己的控制层。
两种方案底层分别由 components.tsx 的插槽覆盖机制与 TldrawUi.tsx 的 hideUi 条件渲染分支支撑,理解了这套机制,你就能在「全默认 UI」「混合定制 UI」「完全 headless」三种形态之间自由切换。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00