Files
upstream-twenty/packages/twenty-docs/l/es/developers/extend/apps/layout/page-layouts.mdx
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

159 lines
9.3 KiB
Plaintext

---
title: Diseños de Página
description: Personaliza las páginas de detalle de los registros — pestañas, widgets y dónde se renderizan los componentes de frontend — usando `definePageLayout` y `definePageLayoutTab`.
icon: table-columns
---
Un **page layout** controla cómo se organiza la página de detalle de un registro: qué pestañas aparecen y qué widgets contienen. Usa `definePageLayout()` para declarar un layout para un objeto que posees, o `definePageLayoutTab()` para agregar una sola pestaña a un layout que ya existe (tuyo o uno estándar de Twenty).
| Caso de uso | Entidad |
| -------------------------------------------------------------------------- | --------------------- |
| Define todo el layout para una página de registro en un objeto que posees | `definePageLayout` |
| Agrega una pestaña a un layout existente (tu propio objeto o uno estándar) | `definePageLayoutTab` |
## definePageLayout
Usa esto cuando eres propietario de toda la página de detalle; normalmente para un objeto personalizado que definiste tú mismo.
```ts src/page-layouts/example-record-page-layout.ts
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
export default definePageLayout({
universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
name: 'Example Record Page',
type: 'RECORD_PAGE',
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
tabs: [
{
universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5',
title: 'Hello World',
position: 50,
icon: 'IconWorld',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d',
title: 'Hello World',
type: 'FRONT_COMPONENT',
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
},
},
],
},
],
});
```
### Puntos clave
* `type` es uno de `'RECORD_INDEX'`, `'RECORD_PAGE'`, `'DASHBOARD'` o `'STANDALONE_PAGE'`. Use `'RECORD_PAGE'` para personalizar la vista de detalles de un objeto específico.
* `objectUniversalIdentifier` especifica a qué objeto se aplica este diseño.
* Cada `tab` define una sección de la página con un `title`, `position` y `layoutMode`: `VERTICAL_LIST` para páginas de registros y páginas independientes, `GRID` para paneles y `CANVAS` para un único widget que debe ocupar el área de visualización de la pestaña. Una pestaña `VERTICAL_LIST` apila los widgets verticalmente. Los widgets integrados que gestionan su propio desplazamiento, como las líneas de tiempo, los archivos, las notas, las tareas y los flujos de trabajo, ocupan un área de visualización; los campos, los componentes front, los gráficos y otros widgets de ajuste al contenido se representan a la altura de su contenido o a la altura configurada. Una pestaña `GRID` siempre distribuye sus widgets como tarjetas en una cuadrícula de 12 columnas. Un widget `CANVAS` no tiene una posición explícita; si una pestaña de lienzo contiene varios widgets, se representan a la altura de su contenido en lugar de llenar la ventana gráfica.
* Establece `layoutMode` explícitamente. Omitirlo te da `VERTICAL_LIST` en una `STANDALONE_PAGE` y `GRID` en todos los demás casos, lo cual rara vez es lo que quieres en una página de registro.
* Cada `widget` dentro de una pestaña puede renderizar un [componente de frontend](/l/es/developers/extend/apps/layout/front-components), una lista de relaciones u otros tipos de widget integrados.
* Un widget `FRONT_COMPONENT` puede establecer `headerCommandMenuItemUniversalIdentifiers` en una matriz ordenada de identificadores universales de elementos de menú de comandos de la misma aplicación. Estas acciones aparecen como botones de icono en el encabezado de la tarjeta del widget y conservan sus comprobaciones de disponibilidad y permisos a nivel de comando. Los identificadores deben ser únicos y deben resolverse cuando se instala la aplicación.
* `position` en las pestañas controla su orden. Usa valores más altos (p. ej., 50) para colocar pestañas personalizadas después de las integradas.
### Widgets de campos
Un widget `FIELD` representa un campo del registro. Para los campos de relación, también puede incrustar una lista de registros relacionados:
```ts
{
universalIdentifier: 'c1c2c3c4-c5c6-4000-8000-000000000003',
title: 'People → Opportunities',
type: 'FIELD',
configuration: {
configurationType: 'FIELD',
fieldMetadataId: PEOPLE_FIELD_UNIVERSAL_IDENTIFIER,
fieldDisplayMode: 'TABLE',
nestedRelationFieldMetadataId: OPPORTUNITIES_FIELD_UNIVERSAL_IDENTIFIER,
},
}
```
* `fieldMetadataId` toma el identificador universal de un campo en el objeto del layout.
* `fieldDisplayMode` es uno de `'FIELD'`, `'CARD'`, `'EDITOR'`, `'VIEW'` o `'TABLE'`. `TABLE` incrusta una vista que lista los registros de un campo de relación de uno a muchos.
* `nestedRelationFieldMetadataId` es opcional y toma el identificador universal de un campo de relación de uno a muchos en el objeto de destino de la relación, para enumerar registros a dos saltos de relación (por ejemplo, una página de Company que enumera las oportunidades de las personas de la compañía, o una página de Person que enumera las oportunidades de la compañía de la persona). El primer salto puede ser un campo de relación de uno a muchos o de muchos a uno, el segundo debe ser de uno a muchos (las relaciones de unión no son compatibles) y requiere `fieldDisplayMode: 'TABLE'` — combinarlo con cualquier otro modo de visualización es un error de validación, ya que un widget anidado siempre se representa como una vista incrustada.
## definePageLayoutTab
Usa esto cuando solo quieras **agregar** una pestaña a un layout existente; por ejemplo, una pestaña de analíticas en la página estándar de Company o una pestaña de resumen de IA añadida al layout de tu propio objeto.
```ts src/page-layouts/example-extra-tab.ts
import {
definePageLayoutTab,
PageLayoutTabLayoutMode,
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
export default definePageLayoutTab({
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
pageLayoutUniversalIdentifier:
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
.universalIdentifier,
title: 'Hello World',
position: 1000,
icon: 'IconWorld',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002',
title: 'Hello World',
type: 'FRONT_COMPONENT',
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
},
},
],
});
```
### Puntos clave
* `pageLayoutUniversalIdentifier` es **obligatorio** y debe apuntar a un page layout que ya exista en el momento de la instalación, ya sea un layout estándar de Twenty o uno definido por tu propia app. Las referencias entre apps a layouts que pertenecen a otra app instalada no son compatibles hoy en día. Cuando falta el layout padre, la instalación falla con un error de validación claro.
* Para los diseños estándar de Twenty, importa los identificadores desde `twenty-sdk/define`:
```ts
import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
// …
```
Cada entrada de diseño también expone sus `tabs` y sus `widgets`, para que puedas hacer referencia a cualquier nivel:
```ts
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home
.universalIdentifier;
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets
.fields.universalIdentifier;
```
También hay disponible un alias corto `STANDARD_PAGE_LAYOUT`:
```ts
import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';
STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
```
* `widgets` están limitados solo a esta pestaña: hacen referencia a [componentes de frontend](/l/es/developers/extend/apps/layout/front-components), vistas, etc., exactamente igual que los widgets definidos en línea en `definePageLayout`.
* `position` controla el orden con respecto a las pestañas existentes en el diseño de página de destino. Elige un valor que sitúe tu pestaña donde la quieras, en relación con las pestañas integradas.
* Usa esto en lugar de `definePageLayout` cuando solo quieras agregar a un layout existente. Usa `definePageLayout` cuando eres propietario de todo el layout.