Dónde se pueden usar los componentes de front
Los componentes de front pueden renderizarse en tres ubicaciones dentro de Twenty:- Panel lateral — Los componentes de front no headless se abren en el panel lateral derecho. Este es el comportamiento predeterminado cuando un componente de front se activa desde el menú de comandos.
- Widgets (tableros y páginas de registros) — Los componentes de front pueden incrustarse como widgets dentro de los diseños de página. Al configurar un tablero o el diseño de una página de registro, los usuarios pueden agregar un widget de componente de front.
- Configuración de la app — Definido con
defineSettingsFrontComponent(), el componente de front se renderiza como una sección dentro de la pestaña Settings de la app, en lugar de la interfaz de configuración de variables predeterminada.
- Emparejarlo con un elemento del menú de comandos: lo registra en el menú de comandos (Cmd+K) y, de forma opcional, como una acción rápida fijada.
- Incrustarlo como widget en un diseño de página: lo coloca en la página de detalles de un registro o en un tablero.
- Definirlo con
defineSettingsFrontComponent()— lo renderiza como una sección dentro de la pestaña Settings de la app, en lugar de la interfaz de configuración de variables predeterminada.
Ejemplo básico
La forma más rápida de ver un componente de front en acción es emparejarlo con undefineCommandMenuItem, de modo que aparezca como un botón de acción rápida en la esquina superior derecha de la página:
src/front-components/hello-world.tsx
src/command-menu-items/hello-world.command-menu-item.ts
yarn twenty dev (o ejecutar una sola vez yarn twenty apply), la acción rápida aparece en la esquina superior derecha de la página:

Campos de configuración
Colocar un componente de frontend en una página
Más allá de los comandos, puedes incrustar un componente de frontend directamente en una página de registro agregándolo como un widget en un diseño de página. Consulta Diseños de página para más detalles.Componente de configuración personalizada
Para reemplazar la interfaz de configuración de variables autogenerada en la pestaña Settings de tu app con tu propio componente, defínelo condefineSettingsFrontComponent en lugar de defineFrontComponent. Toma los mismos campos de configuración (excepto isHeadless, que no se acepta ya que un componente de configuración siempre renderiza una interfaz de usuario visible) y, además, marca el componente como la interfaz de configuración de la aplicación.
El componente se renderiza como una sección dentro de la pestaña Settings, no como un reemplazo de toda la pestaña. Las secciones gestionadas por el sistema de Twenty — actualización automática, App URL y conexiones — siempre se renderizan por encima de ella y no pueden ser sobrescritas por la aplicación.
src/front-components/app-settings.tsx
Headless vs no headless
Los componentes de front vienen en dos modos de renderizado controlados por la opciónisHeadless:
No headless (predeterminado) — El componente renderiza una UI visible. Cuando se activa desde el menú de comandos, se abre en el panel lateral. Este es el comportamiento predeterminado cuando isHeadless es false o se omite.
Headless (isHeadless: true) — El componente se monta de forma invisible en segundo plano. No abre el panel lateral. Los componentes headless están diseñados para acciones que ejecutan lógica y luego se desmontan — por ejemplo, ejecutar una tarea asíncrona, navegar a una página o mostrar un modal de confirmación. Se combinan de forma natural con los componentes Command del SDK descritos a continuación.
src/front-components/sync-tracker.tsx
null, Twenty omite renderizar un contenedor para él — no aparece espacio vacío en el diseño. El componente sigue teniendo acceso a todos los hooks y a la API de comunicación con el host.
Componentes Command del SDK
El paquetetwenty-sdk proporciona cuatro componentes auxiliares Command diseñados para componentes de front headless. Cada componente ejecuta una acción al montarse, gestiona los errores mostrando una notificación tipo snackbar y desmonta automáticamente el componente de front al finalizar.
Impórtalos desde twenty-sdk/front-component:
Command— Ejecuta un callback asíncrono mediante la propexecute.CommandLink— Navega a una ruta de la aplicación. Props:to,params,queryParams,options.CommandModal— Abre un modal de confirmación. Si el usuario confirma, ejecuta el callbackexecute. Props:title,subtitle,execute,confirmButtonText,confirmButtonAccent.CommandOpenSidePanelPage— Abre una página del panel lateral. Las props dependen depage— por ejemplo,ViewRecordreciberecordId+objectNameSingular(además de un id opcional detabpara abrir el registro en una pestaña específica), otras páginas recibenpageTitle+pageIcon.
Command para ejecutar una acción desde el menú de comandos:
src/front-components/run-action.tsx
src/command-menu-items/run-action.command-menu-item.ts
CommandModal para pedir confirmación antes de ejecutar:
src/front-components/delete-draft.tsx
CommandOpenSidePanelPage para abrir el registro actual en el panel lateral en una pestaña específica. tab es un id de pestaña de diseño de página (los diseños predeterminados usan ids como company-tab-emails o company-tab-timeline; los diseños personalizados usan el id propio de la pestaña). Si el id no existe en el diseño del registro, en su lugar se abre la pestaña predeterminada:
src/front-components/open-company-emails.tsx
Llamar a una función de lógica
Los componentes de front se ejecutan en el navegador dentro de un Web Worker aislado (sandboxed) dentro de un iframe de origen opaco, mientras que las funciones de lógica se ejecutan en el servidor. No hay una llamada directa en el mismo proceso entre ambos; en su lugar, un componente de front accede a una función de lógica a través de HTTP. Una función de lógica declarada conhttpRouteTriggerSettings es accesible por HTTP en su ruta. RestApiClient trata las rutas que comienzan con /s/ como rutas de la aplicación, las resuelve a la URL desde la que se sirven tus funciones y las autentica con TWENTY_APP_ACCESS_TOKEN.
En Twenty Cloud, las funciones de lógica activadas por HTTP se sirven en un dominio dedicado por espacio de trabajo en https://\<your-workspace-subdomain>.withtwenty.com\<path>. Para clientes externos, copia la URL exacta desde la configuración de HTTP trigger de la función o desde la pestaña Settings de la aplicación.
Un componente de front sin interfaz (headless) puede ejecutar la llamada al montar mediante el componente Command y luego desmontarse automáticamente:
src/front-components/sync-prs.tsx
RestApiClient es el httpRouteTriggerSettings.path de la función lógica, con el prefijo /s. Mantén isAuthRequired: true; el TWENTY_APP_ACCESS_TOKEN que Twenty crea para tu componente autentica la solicitud:
src/logic-functions/fetch-prs.logic-function.ts
TWENTY_APP_ACCESS_TOKEN se inyecta automáticamente; consulta Variables de la aplicación. Dado que las variables de aplicación secretas nunca se exponen a los componentes de front, mantén las claves de API y otra lógica confidencial en la función de lógica, no en el componente de front.Llamar a la API REST de Twenty
Para llamar a rutas HTTP de la aplicación o leer y escribir registros de Twenty desde un componente de interfaz, utilizaRestApiClient de twenty-client-sdk/rest. Envía las rutas /s/... a la URL base de las funciones de tu espacio de trabajo y cualquier otra ruta, incluidas /rest/..., a TWENTY_API_URL.
options acepta headers, query (un registro de parámetros de cadena de consulta; los valores nulos o indefinidos se omiten) y un AbortSignal mediante signal. Un objeto body que no sea de tipo FormData se serializa automáticamente como JSON. Ante un 401, el cliente actualiza el token de acceso una vez a través del host y vuelve a intentar la solicitud.
La URL base y el token se resuelven desde el entorno de forma predeterminada. Pasa opciones de sobrescritura al constructor cuando sea necesario — por ejemplo, en pruebas:
RestApiClientError que expone status, statusText, url y el body analizado:
Acceder al contexto de ejecución
Dentro de tu componente, usa hooks del SDK para acceder al usuario actual, el registro y la instancia del componente:src/front-components/record-info.tsx
Variables de aplicación
Las variables de aplicación definidas endefineApplication() con isSecret: false están disponibles dentro de los componentes de front mediante la utilidad getApplicationVariable:
src/front-components/greeting.tsx
getApplicationVariable siempre devuelve una cadena (o undefined), independientemente del type declarado de la variable. La cadena se serializa de forma coherente según el tipo (booleanos como "true" / "false", números como cadenas decimales, arrays / objetos como JSON), el mismo formato que se usa para la función lógica process.env — parsea tú mismo (Number(...), JSON.parse(...), === 'true'). Consulta Tipos de variables.
Las siguientes variables de sistema siempre están disponibles a través de process.env:
TWENTY_FUNCTIONS_URL
Twenty también inyecta TWENTY_FUNCTIONS_URL en los componentes de interfaz y en las funciones lógicas: la URL base desde la que se sirven las funciones de lógica activadas por HTTP de tu aplicación.
Existe porque esa URL no siempre es el propio servidor de Twenty. En Twenty Cloud, las rutas de la aplicación se sirven en un dominio dedicado por espacio de trabajo (https://\<your-workspace-subdomain>.withtwenty.com, o el dominio público principal de la aplicación cuando se configure uno) para que las respuestas definidas por la aplicación se ejecuten en un origen aislado en lugar de en el origen de la aplicación de Twenty. Las instancias autoalojadas y locales sirven las rutas de la aplicación bajo el prefijo /s en el propio servidor y es posible que no establezcan la variable en absoluto. Dado que la URL base varía por espacio de trabajo y por instancia, tu código no puede codificarla de forma rígida: el servidor inyecta el valor correcto en tiempo de ejecución.
Rara vez necesitas leerla directamente. Llama a tus rutas a través de RestApiClient con una ruta con el prefijo /s/ y el cliente resuelve la URL por ti: elimina el prefijo /s y apunta a TWENTY_FUNCTIONS_URL, recurriendo a \<TWENTY_API_URL>/s cuando la variable no está establecida. Utiliza resolveUrl('/s/\<path>') para obtener la URL absoluta sin enviar una solicitud, por ejemplo, para un enlace. Lee la variable directamente solo cuando construyas una URL manualmente:
API de comunicación con el host
Los componentes de frontend pueden activar navegación, modales y notificaciones usando funciones detwenty-sdk:
Aquí tienes un ejemplo que usa la API del host para mostrar un snackbar y cerrar el panel lateral después de que una acción finaliza:
src/front-components/archive-record.tsx
Trabajar con varios registros
UsauseSelectedRecordIds() para manejar varios registros seleccionados. Esto es útil para operaciones por lotes:
src/front-components/bulk-export.tsx
src/command-menu-items/bulk-export.command-menu-item.ts
Recursos públicos
Los componentes de frontend pueden acceder a archivos del directoriopublic/ de la aplicación usando getPublicAssetUrl:
Estilo
Los componentes de frontend admiten varios enfoques de estilos. Puedes usar:- Estilos en línea —
style={{ color: 'red' }} - Componentes de UI de Twenty: la propia biblioteca de componentes de Twenty; consulta Uso de los componentes de UI de Twenty más abajo
- Emotion — CSS-in-JS con
@emotion/react - Styled-components — patrones de
styled.div - Tailwind CSS — clases utilitarias
- Cualquier librería CSS-in-JS compatible con React
Uso de los componentes de UI de Twenty
Twenty distribuye su biblioteca de componentes como el paquetetwenty-ui. Los componentes de frontend pueden usarlo para botones, etiquetas, pastillas de estado, chips, avatares, iconos, tipografía y tokens de tema que coinciden automáticamente con el tema claro y oscuro del espacio de trabajo.
Instalación
Añade el paquete a tu aplicación, fijado a la versión con la que se entrega tu instancia de Twenty:twenty-ui se incluye en tu componente de frontend en tiempo de compilación, así que solo necesita ser una dependencia de tu aplicación: no hay nada que configurar en tiempo de ejecución.
Importar componentes
Importa desde la subruta correspondiente en lugar de la raíz del paquete, de modo que solo los componentes que utilizas terminen en tu bundle:Iconos
Importa iconos individuales desdetwenty-ui/icon:
IconsProvider, useIcons e iconsState, ya que incorporan el conjunto completo de iconos Tabler (varios MB).
Temas y tokens de tema
Los componentes de Twenty UI coinciden automáticamente con el tema claro y oscuro del espacio de trabajo: el renderizador aplica el esquema de color activo en el host y los componentes resuelven sus colores en función de este. Para usar los mismos tokens de diseño en tus propios estilos en línea, llama al hookuseTheme(). Devuelve los tokens de tema de Twenty (espaciado, colores, radios, fuentes) conectados al tema activo, sin necesidad de configurar ThemeProvider en tu componente:
useTheme() es un hook, lees los tokens dentro del cuerpo del componente, por lo que los valores siempre reflejan el tema activo. El mismo mapa de tokens también se exporta como la constante themeCssVariables, pero es preferible usar useTheme() en los componentes de frontend: una constante a nivel de módulo que desreferencie themeCssVariables puede ser indefinida mientras se extrae el manifiesto de la aplicación.
Para hacer bifurcaciones explícitamente según el esquema activo, léelo con useColorScheme() de twenty-sdk/front-component, que devuelve 'light' o 'dark'.
Limitaciones actuales
Los componentes de Front están en desarrollo activo. El renderizado, el estilo y el manejo de eventos funcionan bien. Cualquier cosa que vaya más allá del renderizado (medir un elemento, llamar a un método del DOM en un ref, hacer portal fuera de tu árbol, acceder al almacenamiento del navegador) falta o está incompleta hoy, y la mayoría falla silenciosamente: no hay excepción ni error de TypeScript tampoco, ya que el andamiaje está tipado contra el DOM completo del navegador. Si una de estas cosas te bloquea, abre una incidencia para que se priorice.Diseño y medición
Nada puede medirse a sí mismo todavía.
Así que
ResponsiveContainer de recharts, Floating UI / Popper, la virtualización de listas y arrastrar-para-redimensionar todavía no funcionan. Haz el diseño en CSS en su lugar: tu hoja de estilos llega a la página real, por lo que flexbox, grid, aspect-ratio, clamp() y @container se comportan con normalidad.
requestAnimationFrame, fetch, setTimeout y queueMicrotask funcionan sin el prefijo window.. Solo window.requestAnimationFrame(...) y similares lanzan una excepción.Acceso al DOM
Unref te da un elemento sandbox, no un HTMLElement.
La brecha del portal es la razón por la que los popovers de Radix, Headless UI, MUI y react-select no renderizan nada de forma predeterminada. La mayoría acepta una prop de contenedor; apúntala a un elemento que hayas renderizado.
Eventos
Eventos de ratón, puntero, tacto, arrastre, teclado, foco,input/change/submit, scroll/wheel/contextmenu y animationend/transitionend pasan al host, además de algunos específicos por elemento: load/error en <img>, portapapeles y composición en <input>/\<textarea>, medios en \<video>/\<audio>, toggle en \<details>/\<dialog>. Cualquier otra cosa (onAuxClick, onSelect, onInvalid, onReset, onAnimationStart, captura de puntero, onLoad fuera de <img>) se descarta sin aviso.
document.addEventListener() y window.addEventListener() se registran sin error y nunca se disparan, lo que explica por qué un arrastre se detiene tan pronto como el puntero sale del elemento en el que comenzó. event.preventDefault() tampoco cruza; el envío de formularios, dragover/drop y los clics en enlaces ya están protegidos por ti.
Atributos y estilos
Cada elemento reenvía sus propias propiedades al DOM del host (href en \<a>, src/alt en <img>, value/placeholder/disabled en <input>, etc.), además de un conjunto común en cada elemento: id, className, style, title, tabIndex, role, draggable y cualquier atributo aria-* / data-* (con guiones, por lo que ariaLabel se descarta). Cualquier cosa fuera de eso se descarta silenciosamente, así que expresa el estado personalizado como data-*.
El CSS del componente, ya sea desde import './styles.css', CSS-in-JS o un elemento \<style>, se inyecta en el \<head> de la página del host sin ámbito. Así que los nombres de clase colisionan con los de Twenty (ponles un prefijo y nunca escribas div { ... } como selectores), y @media coincide con la ventana del navegador en lugar de con tu widget (usa @container con tu propio container-type). Las props de style en línea no se ven afectadas.
Almacenamiento y red
localStorage, sessionStorage, IndexedDB, las cookies, la Cache API y BroadcastChannel no están disponibles, ya que el componente se ejecuta en un worker en un origen opaco. Para persistir el estado, llama a una función de lógica y usa su almacenamiento de clave-valor.
fetch funciona, con matices:
- Las llamadas a la API de Twenty y a las rutas de tu aplicación se hacen por proxy a través del host, así que da prioridad a
RestApiClient. En las llamadas con proxy,AbortSignaly las demás opciones deRequestInitse descartan, y solo se admiten cuerpos de tipostringyURLSearchParams. - Otros orígenes salen del sandbox con
Origin: null, por lo que una API de terceros responde solo si envíaAccess-Control-Allow-Origin: *. Llámala desde una función de lógica en su lugar. fetch('/rest/people')nunca se hace coincidir con la API de Twenty, porque el sandbox no tiene URL de página contra la que resolver una ruta relativa.
Otras carencias
- Contenido de archivos.
<input type="file">solo proporciona a tu manejador los metadatos del archivo, no los bytes, por lo queFileReadery las subidas no son posibles todavía. - Cargas útiles de arrastrar y soltar. Los eventos de arrastre se disparan, pero
event.dataTransferesundefined. - Integraciones nativas de Node.
fs,pathynode:cryptohacen que la compilación falle, así que mueve ese trabajo a una función de lógica. Web Crypto,fetch,TextEncoderyURLestán disponibles. \<iframe>siempre se vuelve a aislar sinallow-same-origin, por lo que un embed que depende de su propia sesión se renderiza como desconectado. Tampoco tieneonLoad.