From 40dd01c47df126629fc2b8e279dd354234760d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:01:35 +0200 Subject: [PATCH] Document current front component limitations (#23549) Front components are still under active development, but the docs did not say so, and two of the three field reports we got on Discord were misdiagnosed because the sandbox fails silently. Adds a "Current limitations" section to the front components page covering layout measurement, DOM access, events, CSS scoping, storage and network, with the workaround for each. Also corrects the testing page, which claimed front components get "browser APIs" when the sandbox only implements a partial DOM. Every limitation was checked against the code rather than copied from the roadmap, which turned up a few stale entries: CSS imports work, `aria-*`/`data-*` now cross, and `MutationObserver` throws on `.observe()` rather than silently never firing. Review in cubic --- .../extend/apps/layout/front-components.mdx | 73 +++++++++++++++++++ .../extend/apps/layout/overview.mdx | 2 + .../extend/apps/operations/testing.mdx | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) 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 ``/`