前端组件可用位置
在 Twenty 中,前端组件可以在三个位置进行渲染:- 侧边栏 — 非无头的前端组件会在右侧侧边栏中打开。 当前端组件从命令菜单触发时,这是默认行为。
- 小部件(仪表盘和记录页面) — 前端组件可以作为小部件嵌入到页面布局中。 在配置仪表盘或记录页面布局时,用户可以添加前端组件小部件。
- 应用设置 — 使用
defineSettingsFrontComponent()定义后,该前端组件将作为一个部分渲染在应用的 Settings 选项卡中,以替代默认的变量配置界面。
- 将它与命令菜单项配对 —— 将其注册到命令菜单(Cmd+K)中,并可选地将其设为固定快速操作。
- 将它作为小部件嵌入到页面布局中 —— 将其放置在记录详情页面或仪表盘上。
- 使用
defineSettingsFrontComponent()定义 — 将其渲染为应用 Settings 选项卡中的一个部分,以替代默认的变量配置界面。
基础示例
最快看到前端组件实际效果的方式是将它与defineCommandMenuItem配对,这样它就会显示为页面右上角的快速操作按钮:
src/front-components/hello-world.tsx
src/command-menu-items/hello-world.command-menu-item.ts
yarn twenty dev 同步后(或单次运行 yarn twenty apply),快速操作会出现在页面右上角:

配置字段
在页面上放置前端组件
除了命令之外,你还可以在页面布局中将其添加为小部件,从而将前端组件直接嵌入记录页面。 详情请参见页面布局。自定义设置组件
要在应用的 Settings 选项卡中,用你自己的组件替换自动生成的变量配置界面,请使用defineSettingsFrontComponent 而不是 defineFrontComponent 来进行定义。 它使用相同的配置字段(除了 isHeadless,由于设置组件始终会渲染可见的 UI,因此不接受该字段),并另外将该组件标记为应用的设置 UI。
该组件会渲染为设置选项卡内的一个部分,而不是替换整个选项卡。 Twenty 的系统管理部分——自动升级、App URL 和连接——始终渲染在它上方,且无法被应用覆盖。
src/front-components/app-settings.tsx
无头与非无头
前端组件有两种由isHeadless 选项控制的渲染模式:
非无头(默认) — 该组件会渲染可见的 UI。 从命令菜单触发时,它会在侧边栏中打开。 当 isHeadless 为 false 或被省略时,这是默认行为。
无头 (isHeadless: true) — 该组件会在后台以不可见的方式挂载。 它不会打开侧边栏。 无头组件旨在用于执行逻辑后自行卸载的操作——例如运行异步任务、导航到某个页面或显示确认模态框。 它们与下文介绍的 SDK Command 组件天然契合。
src/front-components/sync-tracker.tsx
null,Twenty 会跳过为其渲染容器——布局中不会出现空白区域。 该组件仍可访问所有 hooks 和宿主通信 API。
SDK Command 组件
twenty-sdk 包提供了四个为无头前端组件设计的 Command 辅助组件。 每个组件都会在挂载时执行一个操作,通过显示 snackbar 通知来处理错误,并在完成后自动卸载该前端组件。
从 twenty-sdk/front-component 导入它们:
Command— 通过execute属性运行异步回调。CommandLink— 导航到某个应用路径。 属性:to、params、queryParams、options。CommandModal— 打开一个确认模态框。 如果用户确认,则执行execute回调。 属性:title、subtitle、execute、confirmButtonText、confirmButtonAccent。CommandOpenSidePanelPage— 打开一个侧边栏页面。 Props 取决于page—— 例如,ViewRecord需要recordId+objectNameSingular(以及一个可选的tabid,用于在特定标签页中打开该记录),其他页面需要pageTitle+pageIcon。
Command 从命令菜单运行一个操作:
src/front-components/run-action.tsx
src/command-menu-items/run-action.command-menu-item.ts
CommandModal 在执行前请求确认:
src/front-components/delete-draft.tsx
CommandOpenSidePanelPage 的示例,用于在特定标签页的侧边栏中打开当前记录。 tab 是页面布局的标签页 id(默认布局使用类似 company-tab-emails 或 company-tab-timeline 这样的 id;自定义布局使用标签页自身的 id)。 如果该 id 在记录的布局中不存在,则会改为打开默认标签页:
src/front-components/open-company-emails.tsx
调用逻辑函数
前端组件在浏览器端的 Web Worker 中运行,该 Web Worker 被沙盒化在不透明来源的 iframe 中,而逻辑函数在服务器端运行。 二者之间没有直接的进程内调用——前端组件通过 HTTP 访问逻辑函数。 使用httpRouteTriggerSettings 声明的逻辑函数,可以通过其路由路径在 HTTP 上进行访问。 RestApiClient 会将以 /s/ 开头的路径视为应用路由,将其解析到你的函数提供服务的 URL,并使用 TWENTY_APP_ACCESS_TOKEN 对其进行认证。
在 Twenty Cloud 上,HTTP 触发的逻辑函数通过每个工作区的专用域名提供服务,域名为 https://\<your-workspace-subdomain>.withtwenty.com\<path>。 对于外部调用方,请从函数的 HTTP trigger 设置或应用的 Settings 选项卡中复制准确的 URL。
无头前端组件可以通过 Command 组件在挂载时执行调用,然后自动卸载:
src/front-components/sync-prs.tsx
RestApiClient 的路径是逻辑函数的 httpRouteTriggerSettings.path,并以 /s 作为前缀。 保持 isAuthRequired: true;Twenty 为你的组件生成的 TWENTY_APP_ACCESS_TOKEN 会对请求进行认证:
src/logic-functions/fetch-prs.logic-function.ts
TWENTY_APP_ACCESS_TOKEN 会被自动注入——参见 应用变量。 由于机密应用变量永远不会暴露给前端组件,请将 API 密钥和其他敏感逻辑保留在逻辑函数中,而不是前端组件中。调用 Twenty REST API
要在前端组件中调用应用 HTTP 路由或读取、写入 Twenty 记录,请使用来自twenty-client-sdk/rest 的 RestApiClient。 它会将 /s/... 路径发送到你工作区的函数基础 URL,而将包括 /rest/... 在内的其他所有路径发送到 TWENTY_API_URL。
options 接受 headers、query(查询字符串参数记录;空值会被跳过),以及通过 signal 传入的 AbortSignal。 非 FormData 类型的对象 body 会被自动进行 JSON 序列化。 在收到 401 时,客户端会通过宿主刷新一次访问令牌,然后重试该请求。
基础 URL 和令牌默认会从环境中解析得到。 在需要时将覆盖项传递给构造函数——例如在测试中:
RestApiClientError,其中包含 status、statusText、url 和已解析的 body:
访问运行时上下文
在组件内部,使用 SDK 的 hooks 获取当前用户、记录和组件实例:src/front-components/record-info.tsx
应用程序变量
在defineApplication() 中定义、且 isSecret: false 的应用程序变量,可以通过 getApplicationVariable 实用工具在前端组件中使用:
src/front-components/greeting.tsx
type 为何,getApplicationVariable 始终返回一个 string(或 undefined)。 该字符串会按照类型被一致地序列化(布尔值为 "true" / "false",数字为十进制字符串,数组 / 对象为 JSON),与逻辑函数 process.env 使用的格式相同 —— 需要你自行解析(Number(...)、JSON.parse(...)、=== 'true')。 参见变量类型。
以下系统变量始终可以通过 process.env 获取:
TWENTY_FUNCTIONS_URL
Twenty 还会将 TWENTY_FUNCTIONS_URL 注入到前端组件和逻辑函数中:也就是你的应用的 HTTP 触发逻辑函数所提供服务的基础 URL。
之所以存在这个变量,是因为该 URL 并不总是 Twenty 服务器本身。 在 Twenty Cloud 上,应用路由通过每个工作区的专用域名提供服务(https://\<your-workspace-subdomain>.withtwenty.com,或者在已配置时为应用的主公共域名),以便应用生成的响应运行在一个与 Twenty 应用源不同的隔离源上。 自托管和本地实例会在服务器本身的 /s 前缀下提供应用路由,并且可能完全不会设置该变量。 由于基础 URL 会因工作区和实例而异,你的代码不能将其硬编码——服务器会在运行时注入正确的值。
你很少需要直接读取它。 通过 RestApiClient 使用带有 /s/ 前缀的路径来调用你的路由,客户端会为你解析 URL:它会去掉 /s 前缀并将请求发往 TWENTY_FUNCTIONS_URL,在该变量未设置时则回退到 \<TWENTY_API_URL>/s。 使用 resolveUrl('/s/\<path>') 来在不发送请求的情况下获取绝对 URL,例如用于链接。 仅在手动构建 URL 时才直接读取该变量:
宿主通信 API
前端组件可以使用来自twenty-sdk 的函数触发导航、模态框和通知:
下面是一个示例,使用宿主 API 在操作完成后显示一条 snackbar 并关闭侧边栏:
src/front-components/archive-record.tsx
处理多个记录
使用useSelectedRecordIds() 来处理多个已选记录。 这对于批量操作很有用:
src/front-components/bulk-export.tsx
src/command-menu-items/bulk-export.command-menu-item.ts
公共资源
前端组件可以使用getPublicAssetUrl 访问应用的 public/ 目录中的文件:
样式
前端组件支持多种样式方案。 你可以使用:- 内联样式 —
style={{ color: 'red' }} - Twenty UI 组件 — Twenty 自身的组件库;请参阅下文的 使用 Twenty UI 组件
- Emotion — 使用
@emotion/react的 CSS-in-JS - Styled-components —
styled.div模式 - Tailwind CSS — 工具类
- 任何 CSS-in-JS 库(与 React 兼容)
使用 Twenty UI 组件
Twenty 通过twenty-ui 包提供其组件库。 前端组件可以将其用于按钮、标签、状态徽章、Chip、头像、图标、排版,以及能够自动匹配工作区明暗主题的主题令牌。
安装
将该包添加到你的应用中,并固定为你的 Twenty 实例所提供的版本:twenty-ui 会在构建时被打包进你的前端组件中,因此它只需要作为你的应用的依赖——在运行时无需任何配置。
导入组件
请从匹配的子路径而不是包根路径导入,这样只有你使用到的组件才会被打包进你的 bundle:图标
从twenty-ui/icon 导入单个图标:
IconsProvider、useIcons 和 iconsState——它们会引入完整的 Tabler 图标集(数 MB 大小)。
主题和主题令牌
Twenty UI 组件会自动匹配工作区的明暗主题——渲染器会在宿主上应用当前启用的配色方案,组件会基于该方案解析自己的颜色。 要在你自己的行内样式中使用相同的设计令牌,请调用useTheme() hook。 它会返回与当前主题关联的 Twenty 主题令牌(间距、颜色、圆角、字体),你的组件中无需设置 ThemeProvider:
useTheme() 是一个 hook,你需要在组件主体内部读取令牌,因此这些值始终能反映实时的主题。 同一份令牌映射也作为常量 themeCssVariables 导出,但在前端组件中更推荐使用 useTheme()——在应用清单被抽取时,解引用 themeCssVariables 的模块级常量可能会是 undefined。
如果需要显式地根据当前配色方案做分支判断,可从 twenty-sdk/front-component 中使用 useColorScheme() 读取,它会返回 'light' 或 'dark'。