diff --git a/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx b/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx index b6244699f0..c29a65b0a0 100644 --- a/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx +++ b/packages/twenty-docs/developers/extend/apps/layout/front-components.mdx @@ -6,6 +6,10 @@ icon: "window-maximize" Front components are React components that render directly inside Twenty's UI. They run in an **isolated Web Worker** using Remote DOM — your code executes inside a sandboxed, opaque-origin iframe, yet its UI still renders natively in the page rather than being confined to that iframe. + +Front components are still under active development. Your code runs against a partial DOM, not a real browser page, so advanced usages can fail, often silently. See [Current limitations](#current-limitations). + + ## Where front components can be used Front components can render in three locations within Twenty: @@ -696,3 +700,72 @@ const Card = () => { Because `useTheme()` is a hook, you read tokens inside the component body, so the values always reflect the live theme. The same token map is also exported as the `themeCssVariables` constant, but prefer `useTheme()` in front components — a module-level constant that dereferences `themeCssVariables` can be undefined while the app manifest is extracted. To branch on the active scheme explicitly, read it with `useColorScheme()` from `twenty-sdk/front-component`, which returns `'light'` or `'dark'`. + +## Current limitations + +Front components are under active development. Rendering, styling and handling events works well. Anything that reaches *past* rendering (measuring an element, calling a DOM method on a ref, portaling outside your tree, touching browser storage) is missing or incomplete today, and most of it fails silently: no exception, and no TypeScript error either, since the scaffold is typed against the full browser DOM. + +If one of these blocks you, [open an issue](https://github.com/twentyhq/twenty/issues/new/choose) so it gets prioritized. + +### Layout and measurement + +Nothing can measure itself yet. + +| API | What happens | +|-----|--------------| +| `getBoundingClientRect()`, `getClientRects()` | Throws | +| `offsetWidth`, `clientWidth`, `scrollWidth`, `offsetTop`, ... | Silently `undefined`, so `width ?? 0` yields `0` and `width > 600` is always false | +| `ResizeObserver`, `IntersectionObserver` | `ReferenceError` (`typeof` guards do work) | +| `window.matchMedia()`, `window.getComputedStyle()` | Throws | +| `window.innerWidth`, `innerHeight`, `devicePixelRatio` | Silently `undefined` | +| `new MutationObserver(fn)` | Constructs, then `.observe()` throws | + +So recharts `ResponsiveContainer`, Floating UI / Popper, list virtualization and drag-to-resize do not work yet. Do layout in CSS instead: your stylesheet reaches the real page, so flexbox, grid, `aspect-ratio`, `clamp()` and `@container` all behave normally. + + +`requestAnimationFrame`, `fetch`, `setTimeout` and `queueMicrotask` work without the `window.` prefix. Only `window.requestAnimationFrame(...)` and friends throw. + + +### DOM access + +A `ref` gives you a sandbox element, not an `HTMLElement`. + +| What you write | What happens | Use instead | +|----------------|--------------|-------------| +| `ref.current.focus()`, `.click()`, `.select()`, `.setSelectionRange()`, `.scrollIntoView()`, `video.play()` | Throws | Controlled components; read values from `event.target` | +| `element.classList.add(...)` | Throws (`classList` is `undefined`) | Build the `className` string yourself | +| `document.getElementById()`, `getElementsByClassName()`, `createTreeWalker()` | Throws | `querySelector()` / `querySelectorAll()`, which work | +| `document.activeElement` | Always `undefined` | Track focus with `onFocus` / `onBlur` | +| `` | Renders nothing, no error | SVG, or draw offscreen and show an `` | +| `createPortal(node, document.body)` | Renders nothing, while `isConnected` reports success | Overlays inline with `position: absolute`, or pass the library your own container element | + +The portal gap is why Radix, Headless UI, MUI and react-select popovers render nothing by default. Most accept a container prop; point it at an element you rendered. + +### Events + +Mouse, pointer, touch, drag, keyboard, focus, `input`/`change`/`submit`, `scroll`/`wheel`/`contextmenu` and `animationend`/`transitionend` cross to the host, plus a few per element: `load`/`error` on ``, clipboard and composition on ``/`