From d1c95e380ead2510dde9d748d893d6bf82e2fbff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?=
<71827178+bosiraphael@users.noreply.github.com>
Date: Tue, 10 Mar 2026 15:15:56 +0100
Subject: [PATCH] Update front components documentation (#18521)
Update front components documentation
---
.../developers/extend/capabilities/apps.mdx | 249 ++++++++++++++++++
1 file changed, 249 insertions(+)
diff --git a/packages/twenty-docs/developers/extend/capabilities/apps.mdx b/packages/twenty-docs/developers/extend/capabilities/apps.mdx
index 25779d9609..cc37c33692 100644
--- a/packages/twenty-docs/developers/extend/capabilities/apps.mdx
+++ b/packages/twenty-docs/developers/extend/capabilities/apps.mdx
@@ -825,6 +825,255 @@ You can create new front components in two ways:
- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new front component.
- **Manual**: Create a new `.tsx` file and use `defineFrontComponent()`, following the same pattern.
+#### Where front components can be used
+
+Front components can render in two locations within Twenty:
+
+- **Side panel** — Non-headless front components open in the right-hand side panel. This is the default behavior when a front component is triggered from the command menu.
+- **Widgets (dashboards and record pages)** — Front components can be embedded as widgets inside page layouts. When configuring a dashboard or a record page layout, users can add a front component widget.
+
+#### Headless vs non-headless
+
+Front components come in two rendering modes controlled by the `isHeadless` option:
+
+**Non-headless (default)** — The component renders a visible UI. When triggered from the command menu it opens in the side panel. This is the default behavior when `isHeadless` is `false` or omitted.
+
+**Headless** — The component mounts invisibly in the background. It does not open the side panel. Headless components are designed for actions that execute logic and then unmount themselves — for example, running an async task, navigating to a page, or showing a confirmation modal. They pair naturally with the SDK Command components described below.
+
+```typescript
+export default defineFrontComponent({
+ universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
+ name: 'my-action',
+ description: 'Runs an action without opening the side panel',
+ component: MyAction,
+ isHeadless: true,
+ command: {
+ universalIdentifier: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
+ label: 'Run my action',
+ },
+});
+```
+
+#### Adding command menu items
+
+To make a front component appear as an item in Twenty's command menu, add the `command` property to `defineFrontComponent()`. When users open the command menu (Cmd+K / Ctrl+K), the item shows up and triggers the front component on click.
+
+The `command` object accepts the following fields:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `universalIdentifier` | `string` (required) | Unique ID for the command menu item |
+| `label` | `string` (required) | Display label shown in the command menu |
+| `icon` | `string` (optional) | Icon name (e.g., `'IconSparkles'`) |
+| `isPinned` | `boolean` (optional) | Whether the command is pinned at the top of the menu |
+| `availabilityType` | `'GLOBAL' \| 'RECORD_SELECTION'` (optional) | `GLOBAL` shows the command everywhere; `RECORD_SELECTION` shows it only in record contexts |
+| `availabilityObjectUniversalIdentifier` | `string` (optional) | Restrict the command to a specific object type (e.g., Person) |
+
+Here is an example from the call-recording app that adds a command scoped to Person records:
+
+```typescript
+import { defineFrontComponent } from 'twenty-sdk';
+
+export default defineFrontComponent({
+ universalIdentifier: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
+ name: 'Summarize Person Call Recordings',
+ description: 'Generates a summary of call recordings for a person',
+ component: SummarizePersonRecordings,
+ command: {
+ universalIdentifier: 'd4e5f6a7-b8c9-0123-defa-234567890123',
+ label: 'Summarize call recordings',
+ icon: 'IconSparkles',
+ isPinned: false,
+ availabilityType: 'RECORD_SELECTION',
+ availabilityObjectUniversalIdentifier:
+ '20202020-e674-48e5-a542-72570eee7213',
+ },
+});
+```
+
+When the command is synced, it appears in the command menu. If the front component is non-headless the side panel opens with the component rendered inside. If it is headless the component mounts in the background and executes its logic.
+
+#### SDK Command components
+
+The `twenty-sdk` package provides four Command helper components designed for headless front components. Each component executes an action on mount, handles errors by showing a snackbar notification, and automatically unmounts the front component when done.
+
+Import them from `twenty-sdk/command`:
+
+- **`Command`** — Runs an async callback via the `execute` prop.
+- **`CommandLink`** — Navigates to an app path. Props: `to`, `params`, `queryParams`, `options`.
+- **`CommandModal`** — Opens a confirmation modal. If the user confirms, executes the `execute` callback. Props: `title`, `subtitle`, `execute`, `confirmButtonText`, `confirmButtonAccent`.
+- **`CommandOpenSidePanelPage`** — Opens a specific side panel page. Props: `page`, `pageTitle`, `pageIcon`.
+
+Here is a full example of a headless front component using `Command` to run an action from the command menu:
+
+```typescript
+// src/front-components/run-action.tsx
+import { defineFrontComponent } from 'twenty-sdk';
+import { Command } from 'twenty-sdk/command';
+import { CoreApiClient } from 'twenty-sdk/clients';
+
+const RunAction = () => {
+ const execute = async () => {
+ const client = new CoreApiClient();
+
+ await client.mutation({
+ createTask: {
+ __args: { data: { title: 'Created by my app' } },
+ id: true,
+ },
+ });
+ };
+
+ return
Record: {recordId ?? 'none'}
+User: {userId ?? 'anonymous'}
+Archive this record?
+ +