react-x11 2.51.0 → 2.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +87 -54
  2. package/docs/README.md +410 -0
  3. package/docs/accessibility.md +628 -0
  4. package/docs/appearance.md +416 -0
  5. package/docs/architecture/accessibility.md +470 -0
  6. package/docs/architecture/animation.md +707 -0
  7. package/docs/architecture/container-queries.md +525 -0
  8. package/docs/architecture/custom-layout.md +334 -0
  9. package/docs/architecture/drag-and-drop.md +941 -0
  10. package/docs/architecture/element-layer-contents.md +622 -0
  11. package/docs/architecture/frame-pacing.md +255 -0
  12. package/docs/architecture/grid-layout.md +358 -0
  13. package/docs/architecture/protocol-efficiency.md +357 -0
  14. package/docs/architecture/video.md +1149 -0
  15. package/docs/architecture/windowed-regions.md +854 -0
  16. package/docs/click-to-component.md +125 -0
  17. package/docs/clipboard.md +281 -0
  18. package/docs/components.md +1749 -0
  19. package/docs/dbus.md +277 -0
  20. package/docs/debugging.md +464 -0
  21. package/docs/desktop-calendar.md +284 -0
  22. package/docs/desktop.md +648 -0
  23. package/docs/devtools.md +250 -0
  24. package/docs/drag-and-drop.md +663 -0
  25. package/docs/ecosystem/animation.md +394 -0
  26. package/docs/ecosystem/data-fetching.md +201 -0
  27. package/docs/ecosystem/dev-tooling.md +436 -0
  28. package/docs/ecosystem/forms.md +313 -0
  29. package/docs/ecosystem/headless.md +219 -0
  30. package/docs/ecosystem/i18n.md +89 -0
  31. package/docs/ecosystem/icons.md +410 -0
  32. package/docs/ecosystem/layout.md +216 -0
  33. package/docs/ecosystem/routing.md +146 -0
  34. package/docs/ecosystem/state.md +361 -0
  35. package/docs/ecosystem/testing.md +500 -0
  36. package/docs/ecosystem/theming.md +540 -0
  37. package/docs/ecosystem.md +473 -0
  38. package/docs/elements.md +2762 -0
  39. package/docs/embedding.md +288 -0
  40. package/docs/events.md +1025 -0
  41. package/docs/extending.md +2242 -0
  42. package/docs/eyedropper.md +223 -0
  43. package/docs/filedialog.md +258 -0
  44. package/docs/frame.md +580 -0
  45. package/docs/gl.md +170 -0
  46. package/docs/globalmenu.md +278 -0
  47. package/docs/glx.md +137 -0
  48. package/docs/macos.md +3295 -0
  49. package/docs/notifications.md +164 -0
  50. package/docs/packaging.md +611 -0
  51. package/docs/permissions.md +148 -0
  52. package/docs/react-features.md +430 -0
  53. package/docs/remote.md +223 -0
  54. package/docs/scale.md +339 -0
  55. package/docs/security.md +132 -0
  56. package/docs/styling.md +1928 -0
  57. package/docs/system.md +599 -0
  58. package/docs/testing.md +514 -0
  59. package/docs/typescript.md +117 -0
  60. package/docs/uri-schemes.md +380 -0
  61. package/docs/wayland-backend.md +591 -0
  62. package/docs/wayland.md +545 -0
  63. package/docs/windows-embedding.md +271 -0
  64. package/docs/windows-gl.md +221 -0
  65. package/docs/windows-integrations.md +233 -0
  66. package/docs/windows.md +1404 -0
  67. package/package.json +10 -3
  68. package/src/bus.js +39 -26
  69. package/src/cli/create.d.ts +43 -0
  70. package/src/cli/create.js +305 -0
  71. package/src/cli/env.js +484 -0
  72. package/src/cli/index.js +29 -2
  73. package/src/cli/templates/AGENTS.template.md +195 -0
  74. package/src/cli/templates/DESIGN.template.md +269 -0
  75. package/src/desktopintegration.js +2 -1
  76. package/src/desktopsettings.js +193 -24
  77. package/src/filedialog.js +3 -3
  78. package/src/keyboardstate.js +28 -0
  79. package/src/node.d.ts +10 -10
  80. package/src/portal.js +31 -0
  81. package/src/screencolor.js +5 -10
  82. package/src/types/system.d.ts +8 -6
  83. package/src/wayland/app.js +59 -0
  84. package/src/wayland/clipboard.js +14 -7
  85. package/src/wayland/connection.js +3 -0
  86. package/src/wayland/context2d.js +129 -10
  87. package/src/wayland/protocols/index.json +6 -0
  88. package/src/wayland/protocols/xdg-foreign-unstable-v2.json +1 -0
  89. package/src/wayland/seat.js +28 -1
  90. package/src/wayland/swapchain.js +23 -0
  91. package/src/wayland/xkb.js +137 -0
@@ -0,0 +1,473 @@
1
+ # Ecosystem
2
+
3
+ react-x11 is a React renderer with no DOM: there is no `document`, no
4
+ `window` global, no CSSOM, and the host elements are `<box>`/`<text>`/`<svg>`
5
+ rather than `<div>`/`<span>`. So any package that only touches React —
6
+ hooks, context, state, reconciliation — works unchanged, and any package
7
+ that reaches for `document`, `window`, `HTMLElement`, CSS, or renders a
8
+ `<div>` does not.
9
+
10
+ That one rule decides every verdict below. Each of them was established by
11
+ running the library against react-x11 1.2.0, not by reading its
12
+ documentation — the two exceptions are React DevTools and Fast Refresh,
13
+ which are already wired into this repo, so their pages document the working
14
+ setup rather than re-deriving it.
15
+
16
+ ## The 30-second test
17
+
18
+ Before installing anything, ask the four questions in order. Each one has a
19
+ command.
20
+
21
+ 1. **Does it import?** `node --input-type=module -e "import('the-package')"`.
22
+ A package that hard-requires `react-dom`, or whose `exports` map has only
23
+ browser conditions, fails here — see
24
+ [import time](#install-and-import-time) below. Caveat: if anything else in
25
+ your tree already pulled `react-dom` in, this test passes and the problem
26
+ moves to the [silent](#silent-failures) column. Run
27
+ `npm ls react-dom` before you trust a pass.
28
+ 2. **Does it render host elements?** Grep the shipped code for tag names:
29
+
30
+ ```sh
31
+ grep -rEoh '"(div|span|button|input|form|style|a)"' \
32
+ node_modules/the-package --include='*.js' --include='*.mjs' | sort -u
33
+ ```
34
+
35
+ Anything that emits DOM tags cannot render here, no matter how headless
36
+ the README says it is. This one is a reliable verdict: zustand and
37
+ `@tanstack/react-table` print nothing; recharts, react-select and Headless
38
+ UI print a list.
39
+
40
+ 3. **Does it read DOM globals?** Same shape, different pattern:
41
+
42
+ ```sh
43
+ grep -rnE 'typeof window|typeof document|HTMLElement|getBoundingClientRect' \
44
+ node_modules/the-package --include='*.js' --include='*.mjs'
45
+ ```
46
+
47
+ This one is a pointer, not a verdict — _read the hits_. A minified UMD
48
+ bundle and an `isSSR = typeof window === "undefined"` default both match
49
+ harmlessly. What you are looking for is an environment probe whose result
50
+ gates a feature, because that is the silent-failure shape: the library
51
+ decides it is doing SSR and quietly turns things off. Check the transitive
52
+ core package too — `@tanstack/query-core`, not just
53
+ `@tanstack/react-query`, which has no hits of its own.
54
+
55
+ 4. **Does it expect DOM-shaped events?** Since 2.0.0 the answer is usually
56
+ fine: `<textinput onChange>` hands over a synthetic event with
57
+ `target.value`, `target.name` and a writable `target.value`, so
58
+ `event.target.value` reads the way it does in the DOM. What to check
59
+ instead is the **opposite** direction — a library whose field contract is
60
+ value-in/value-out (`field.handleChange(value)`) now needs one unwrap per
61
+ field, and a bare `onChange={field.handleChange}` stores the event object
62
+ with no error. Grep your own code for `onChange={` on a `<textinput>`.
63
+
64
+ A library that passes all four is almost always a drop-in. A library that
65
+ fails 2 or 3 usually has a headless core published separately — that is the
66
+ pattern behind half the adapters on this page.
67
+
68
+ ## What works
69
+
70
+ Fifty-three packages and two Node built-ins, across 12 categories. **Out of
71
+ the box** means install and use; **adapter** means a small amount of glue,
72
+ written out in full on the category page.
73
+
74
+ ### State management
75
+
76
+ | Package | Integration | |
77
+ | -------------------------------------------- | ---------------------------------------------------- | ----------------------------------- |
78
+ | `zustand` | out of the box | [state](ecosystem/state.md#zustand) |
79
+ | `jotai` | out of the box | [state](ecosystem/state.md#jotai) |
80
+ | `valtio` | out of the box | [state](ecosystem/state.md#valtio) |
81
+ | `xstate` + `@xstate/react` | out of the box | [state](ecosystem/state.md#xstate) |
82
+ | `redux` + `@reduxjs/toolkit` + `react-redux` | out of the box — **pin react-redux ≥ 9** | [state](ecosystem/state.md#redux) |
83
+ | `immer` | out of the box | [state](ecosystem/state.md#immer) |
84
+ | `mobx` + `mobx-react-lite` | adapter — no code, but `react-dom` must be installed | [state](ecosystem/state.md#mobx) |
85
+
86
+ ### Data fetching
87
+
88
+ | Package | Integration | |
89
+ | ----------------------- | ------------------ | ---------------------------------------------------------- |
90
+ | `@tanstack/react-query` | adapter, ~12 lines | [data fetching](ecosystem/data-fetching.md#tanstack-query) |
91
+ | `swr` | adapter, 1 line | [data fetching](ecosystem/data-fetching.md#swr) |
92
+
93
+ ### Forms and validation
94
+
95
+ | Package | Integration | |
96
+ | ---------------------- | ---------------------------------------------- | ------------------------------------------- |
97
+ | `@tanstack/react-form` | out of the box | [forms](ecosystem/forms.md#tanstack-form) |
98
+ | `zod` | out of the box | [forms](ecosystem/forms.md#zod) |
99
+ | `react-hook-form` | adapter — use `<Controller>`, not `register()` | [forms](ecosystem/forms.md#react-hook-form) |
100
+
101
+ ### Icons
102
+
103
+ | Package | Integration | |
104
+ | ------------------------------------ | ------------------------------------- | -------------------------------------------- |
105
+ | `@iconify/utils` + `@iconify-json/*` | out of the box | [icons](ecosystem/icons.md#iconify) |
106
+ | `@heroicons/react` | out of the box — size it with `style` | [icons](ecosystem/icons.md#heroicons-react) |
107
+ | `lucide` | adapter, ~10 lines | [icons](ecosystem/icons.md#lucide) |
108
+ | `lucide-static` | adapter, ~12 lines (`iconSource`) | [icons](ecosystem/icons.md#lucide-static) |
109
+ | `react-icons` | adapter, ~15 lines | [icons](ecosystem/icons.md#react-icons) |
110
+ | `@tabler/icons` | adapter — `iconSource` | [icons](ecosystem/icons.md#tabler-icons) |
111
+ | `@phosphor-icons/core` | adapter — `iconSource` | [icons](ecosystem/icons.md#phosphor-icons) |
112
+ | `heroicons` (the asset package) | adapter — `iconSource` | [icons](ecosystem/icons.md#heroicons-assets) |
113
+ | `feather-icons` | adapter, ~5 lines | [icons](ecosystem/icons.md#feather-icons) |
114
+
115
+ ### Theming, palettes and colour
116
+
117
+ | Package | Integration | |
118
+ | ------------------------------------- | ----------------------------------- | -------------------------------------------------------- |
119
+ | `@radix-ui/colors` | out of the box | [theming](ecosystem/theming.md#radix-colors) |
120
+ | `@catppuccin/palette` | out of the box | [theming](ecosystem/theming.md#catppuccin) |
121
+ | `@material/material-color-utilities` | out of the box from a source colour | [theming](ecosystem/theming.md#material-color-utilities) |
122
+ | `colord` | out of the box | [theming](ecosystem/theming.md#colord) |
123
+ | `culori` | out of the box | [theming](ecosystem/theming.md#culori) |
124
+ | `open-props` | out of the box for colours | [theming](ecosystem/theming.md#open-props) |
125
+ | `tailwindcss` (`/colors` export only) | adapter, ~3 lines — oklch → hex | [theming](ecosystem/theming.md#tailwind-colors) |
126
+ | `daltonize` | adapter, ~4 lines | [theming](ecosystem/theming.md#daltonize) |
127
+
128
+ ### Animation
129
+
130
+ | Package | Integration | |
131
+ | ---------------------------------- | --------------------------------- | ---------------------------------------------------------- |
132
+ | `popmotion` | out of the box | [animation](ecosystem/animation.md#popmotion) |
133
+ | `d3-ease` | out of the box | [animation](ecosystem/animation.md#d3-ease) |
134
+ | `d3-interpolate` | out of the box | [animation](ecosystem/animation.md#d3-interpolate) |
135
+ | `@tweenjs/tween.js` | out of the box | [animation](ecosystem/animation.md#tweenjs) |
136
+ | `flubber` | out of the box | [animation](ecosystem/animation.md#flubber) |
137
+ | `react-transition-group` | out of the box — with `nodeRef` | [animation](ecosystem/animation.md#react-transition-group) |
138
+ | `@react-spring/core` + `/animated` | adapter, ~20 lines (`createHost`) | [animation](ecosystem/animation.md#react-spring) |
139
+
140
+ ### Headless components
141
+
142
+ | Package | Integration | |
143
+ | ----------------------- | -------------- | ------------------------------------------------ |
144
+ | `react-stately` | out of the box | [headless](ecosystem/headless.md#react-stately) |
145
+ | `@tanstack/react-table` | out of the box | [headless](ecosystem/headless.md#tanstack-table) |
146
+
147
+ ### Layout and positioning
148
+
149
+ | Package | Integration | |
150
+ | ------------------------ | ---------------------------------------------------- | ---------------------------------------------- |
151
+ | `@floating-ui/core` | adapter, ~35 lines (platform object) | [layout](ecosystem/layout.md#floating-ui) |
152
+ | `@tanstack/virtual-core` | adapter, ~40 lines — the core, not the React wrapper | [layout](ecosystem/layout.md#tanstack-virtual) |
153
+
154
+ ### Routing
155
+
156
+ | Package | Integration | |
157
+ | -------------- | -------------------------------------------- | -------------------------------------------- |
158
+ | `wouter` | out of the box — `wouter/memory-location` | [routing](ecosystem/routing.md#wouter) |
159
+ | `react-router` | out of the box — `MemoryRouter`, no `<Link>` | [routing](ecosystem/routing.md#react-router) |
160
+
161
+ ### Internationalization
162
+
163
+ | Package | Integration | |
164
+ | --------------------------- | -------------- | ------------------------- |
165
+ | `i18next` + `react-i18next` | out of the box | [i18n](ecosystem/i18n.md) |
166
+
167
+ ### Testing
168
+
169
+ | Package | Integration | |
170
+ | ---------------------- | -------------------------------------------------- | --------------------------------------------------- |
171
+ | `node:test` (built-in) | out of the box | [testing](ecosystem/testing.md#runners) |
172
+ | `vitest` | out of the box | [testing](ecosystem/testing.md#vitest) |
173
+ | `msw` | out of the box — `msw/node` | [testing](ecosystem/testing.md#msw) |
174
+ | `fast-check` | out of the box | [testing](ecosystem/testing.md#fast-check) |
175
+ | `sinon` | out of the box — one required `toFake` restriction | [testing](ecosystem/testing.md#sinon) |
176
+ | `pixelmatch` | out of the box | [testing](ecosystem/testing.md#pixelmatch) |
177
+ | `react-test-renderer` | out of the box — but deprecated upstream | [testing](ecosystem/testing.md#react-test-renderer) |
178
+
179
+ ### Developer tooling
180
+
181
+ | Package | Integration | |
182
+ | ------------------------------------------ | ---------------------------------------------------------- | ---------------------------------------------------------- |
183
+ | `react-devtools-core` | out of the box — already wired behind `REACT_X11_DEVTOOLS` | [dev tooling](ecosystem/dev-tooling.md#react-devtools) |
184
+ | `react-refresh` + `hot-module-replacement` | supported — the `react-x11/refresh` entry point | [dev tooling](ecosystem/dev-tooling.md#react-refresh) |
185
+ | `node --inspect` (built-in) | out of the box | [dev tooling](ecosystem/dev-tooling.md#node-inspect) |
186
+ | `0x` | out of the box | [dev tooling](ecosystem/dev-tooling.md#0x) |
187
+ | `@welldone-software/why-did-you-render` | out of the box | [dev tooling](ecosystem/dev-tooling.md#why-did-you-render) |
188
+
189
+ ## What does not work
190
+
191
+ Everything below was reproduced against react-x11 1.2.0. The error text is
192
+ verbatim — if you got here by searching for one of these strings, this is
193
+ the right page.
194
+
195
+ Failures come in four flavours, and they cost very different amounts of
196
+ time:
197
+
198
+ - **Install and import time** — loud, immediate, cheap.
199
+ - **Render time** — loud, immediate, and the message usually names the
200
+ offending element.
201
+ - **First interaction** — the tree mounts clean and breaks on the first
202
+ keystroke. Expensive, because the stack trace points at the library, not
203
+ at the renderer.
204
+ - **Silent** — no error, ever. The most expensive class by a wide margin;
205
+ read that table even if nothing is broken yet.
206
+
207
+ ### Install and import time
208
+
209
+ | Package | Error | Use instead |
210
+ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
211
+ | `react-redux@8` | `npm error code ERESOLVE` … `peer react@"^16.8 \|\| ^17.0 \|\| ^18.0" from react-redux@8.1.3` | `react-redux@9` — [state](ecosystem/state.md#redux) |
212
+ | `react-redux@8` (past ERESOLVE) | `Error: Cannot find module 'react-dom'` from `react-redux/lib/utils/reactBatchedUpdates.js` | `react-redux@9` |
213
+ | `@tanstack/react-virtual` | `Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'react-dom' imported from node_modules/@tanstack/react-virtual/dist/esm/index.js` | `@tanstack/virtual-core` — [layout](ecosystem/layout.md#tanstack-virtual) |
214
+ | `styled-components` | `TypeError: styled.div is not a function` | `createStyles`/`ThemeProvider` + a palette package — [theming](ecosystem/theming.md) |
215
+ | `react-scan` | `Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './auto' is not defined by "exports" in node_modules/react-scan/package.json imported from …` | React's own `<Profiler>`, or why-did-you-render — [dev tooling](ecosystem/dev-tooling.md#why-did-you-render) |
216
+ | `@testing-library/react` (no `react-dom`) | `Error: Cannot find module 'react-dom/test-utils'` | `node:test` or `vitest` — [testing](ecosystem/testing.md) |
217
+ | `jest` (no ESM config) | `Jest encountered an unexpected token` … `SyntaxError: Cannot use import statement outside a module` | `node:test`/`vitest`, or Jest in ESM mode — [testing](ecosystem/testing.md#jest) |
218
+ | `jest` (transformed to CJS) | `SyntaxError: Cannot use 'import.meta' outside a module` | as above |
219
+
220
+ `styled-components` deserves a note because the error is actively
221
+ misleading. It fires at module-evaluation time, mentions neither react-x11
222
+ nor the DOM, and sends people hunting for a version mismatch:
223
+
224
+ ```js
225
+ import styled from 'styled-components';
226
+ const Panel = styled.div`
227
+ background: #1e1e2e;
228
+ `; // throws right here
229
+ ```
230
+
231
+ Under plain Node ESM — which is how react-x11 apps run, with no bundler —
232
+ the default import resolves to the CJS module namespace, so `styled` is an
233
+ object with `ServerStyleSheet` and friends on it, and no `.div`. Working
234
+ around the interop with `const styled = sc.default ?? sc` only moves you to
235
+ `react-x11: unknown element type <div>`. See the
236
+ [silent failures](#silent-failures) table for what happens if you then try
237
+ `styled('box')`.
238
+
239
+ The two Jest entries are the same dead end from two directions. Full text:
240
+
241
+ ```
242
+ Jest encountered an unexpected token
243
+ …
244
+ node_modules/react-x11/src/Reconciler.js:27
245
+ const _require = (0, _nodeModule.createRequire)(import.meta.url);
246
+ ^^^^
247
+ SyntaxError: Cannot use 'import.meta' outside a module
248
+ ```
249
+
250
+ `react-x11/src/Reconciler.js` and `src/DevToolsIntegration.js` both call
251
+ `createRequire(import.meta.url)`, which cannot be down-compiled to CJS. Jest
252
+ _can_ run react-x11 tests, but only in real ESM mode — see
253
+ [testing](ecosystem/testing.md#jest). `node:test` and `vitest` need no
254
+ transform at all.
255
+
256
+ ### Render time
257
+
258
+ The renderer's own errors are prefixed `react-x11:` and name the element it
259
+ could not create. This is the most common failure by far, and the tag it
260
+ names tells you which library layer broke.
261
+
262
+ | Package | Error | Use instead |
263
+ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
264
+ | `framer-motion` | `react-x11: uncaught error Error: react-x11: unknown element type <div>. Supported: <window>, <popup>, <box>, <text>, <image>, <canvas>, <textinput>, <textarea>, <svg>, <glarea>.` | popmotion, `@tweenjs/tween.js`, d3-ease + d3-interpolate — [animation](ecosystem/animation.md) |
265
+ | `framer-motion` (value layer) | `ReferenceError: HTMLElement is not defined` | popmotion — [animation](ecosystem/animation.md#popmotion) |
266
+ | `recharts` | `react-x11: uncaught error Error: react-x11: unknown element type <div>. …` | d3-scale + d3-shape into the `<svg>` host — [below](#charting) |
267
+ | `formik` (`<Form>` with a `<Field>`) | `react-x11: uncaught error Error: react-x11: unknown element type <input>. …` | `@tanstack/react-form` — [forms](ecosystem/forms.md#tanstack-form) |
268
+ | `formik` (`<Form>` alone) | `react-x11: uncaught error Error: react-x11: unknown element type <form>. …` | as above |
269
+ | `@headlessui/react` (`Switch`) | `react-x11: uncaught error Error: react-x11: unknown element type <button>. …` | react-x11's built-in widgets — [components](components.md) |
270
+ | `@headlessui/react` (`Dialog`) | `react-x11: uncaught error Error: react-x11: unknown element type <span>. …` | react-x11's built-in `Dialog` |
271
+ | `@radix-ui/react-dialog` | `react-x11: uncaught error Error: react-x11: unknown element type <button>. …` | react-x11's built-in `Dialog` |
272
+ | `react-select` | `react-x11: uncaught error Error: react-x11: unknown element type <style>. …` | react-x11's built-in `Select` |
273
+ | `@emotion/styled` | `react-x11: uncaught error Error: react-x11: unknown element type <style>. …` | `createStyles` + `ThemeProvider` — [styling](styling.md) |
274
+ | `react-dropzone` | `react-x11: uncaught error Error: react-x11: unknown element type <input>. …` | built-in `useDropTarget` / `<box dropAccept onDrop>` — [drag and drop](drag-and-drop.md) |
275
+ | `react-router-dom` (`BrowserRouter`) | `react-x11: uncaught error ReferenceError: document is not defined` at `getUrlBasedHistory` | `MemoryRouter` — [routing](ecosystem/routing.md#react-router) |
276
+ | `react-router-dom` (`<Link>`) | `react-x11: uncaught error Error: react-x11: unknown element type <a>. …` | `useNavigate()` on a `<box onClick>` |
277
+ | `lucide-react` | `react-x11: uncaught error Error: react-x11: <svg width=…> is a style property — pass it in style: <svg style={{ width: … }} />` | `@iconify-json/lucide`, or `lucide` — [icons](ecosystem/icons.md) |
278
+ | `@tabler/icons-react` | `react-x11: uncaught error Error: react-x11: <svg width=…> is a style property — pass it in style: <svg style={{ width: … }} />` | `@iconify-json/tabler`, or `@tabler/icons` — [icons](ecosystem/icons.md#tabler-icons) |
279
+ | `@phosphor-icons/react` | `react-x11: uncaught error Error: react-x11: <svg width=…> is a style property — pass it in style: <svg style={{ width: … }} />` | `@iconify-json/ph`, or `@phosphor-icons/core` — [icons](ecosystem/icons.md#phosphor-icons) |
280
+ | `@testing-library/react` | `ReferenceError: document is not defined` at `render (node_modules/@testing-library/react/dist/pure.js:256:5)` | `node:test`/`vitest` against the node tree — [testing](ecosystem/testing.md) |
281
+ | `@testing-library/dom` (`within`) | `TypeError: Expected container to be an Element, a Document or a DocumentFragment but got WindowNode.` | walk `node.children`/`node.props` — [testing](ecosystem/testing.md) |
282
+ | `@testing-library/dom` (`screen`) | `TypeError: For queries bound to document.body a global document has to be available... Learn more: https://testing-library.com/s/screen-global-error` | nothing — there is no accessibility tree behind react-x11 today |
283
+ | `@testing-library/user-event` | `TypeError: Cannot read properties of undefined (reading 'Symbol(Node prepared with document state workarounds)')` | emit raw ntk events on the window — [testing](ecosystem/testing.md#driving-events) |
284
+ | `jest-image-snapshot` | `TypeError: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object` | `getImageData` + `pngjs` + `pixelmatch` — [testing](ecosystem/testing.md#pixelmatch) |
285
+ | `react-x11` test selectors | `Error: Test selector API is not supported by this renderer.` | nothing — `supportsTestSelectors` is `false` |
286
+
287
+ Four of those need the detail, because the obvious workaround does not
288
+ work.
289
+
290
+ **framer-motion cannot be rescued by dropping to its value layer.** The
291
+ `motion.div` failure is the expected one; the interesting one is what
292
+ happens when you reach past it:
293
+
294
+ ```js
295
+ import { animate, motionValue } from 'framer-motion';
296
+ const mv = motionValue(0);
297
+ mv.on('change', (v) => console.log(v));
298
+ animate(mv, 100, { duration: 0.1 });
299
+ // ReferenceError: HTMLElement is not defined
300
+ ```
301
+
302
+ `motion-dom` feature-detects the Web Animations API by testing
303
+ `subject instanceof HTMLElement`, so `animate()` on a plain `motionValue`
304
+ still throws once keyframes resolve — asynchronously, from
305
+ `motion-dom/src/animation/waapi/supports/waapi.ts`. Use popmotion's
306
+ `animate()` instead: same author lineage, no DOM assumption.
307
+
308
+ **The Radix escape hatch is worse than the error.**
309
+ `<Dialog.Portal><Dialog.Content /></Dialog.Portal>` produces no error and no
310
+ output at all — Radix's `Portal` is SSR-guarded, finds no `document`, and
311
+ renders nothing, so the dialog silently never appears. Every Radix primitive
312
+ has that shape. (`@radix-ui/colors`, by contrast, is pure data and is
313
+ [recommended](ecosystem/theming.md#radix-colors).)
314
+
315
+ **react-dropzone fails twice.** Deleting the `<input>` does not rescue it:
316
+ with only `<box {...getRootProps()} />` you get past the reconciler and then
317
+ hit `react-x11: uncaught error ReferenceError: window is not defined`,
318
+ thrown from react-dropzone's own mount effect.
319
+
320
+ **The three `*-react` icon packages produce a byte-identical error**, and it
321
+ is not "unknown element type" — react-x11 has a real `<svg>` host, so the
322
+ icon gets further than every other DOM library here and then dies on the
323
+ flat `width`/`height` props those wrappers set unconditionally. There is no
324
+ prop you can pass to avoid it. `@heroicons/react` is the
325
+ [exception](ecosystem/icons.md#heroicons-react): it sizes with a class name
326
+ rather than flat attributes, so it renders.
327
+
328
+ Two more index notes for searchers:
329
+
330
+ - `react-select` reports `<style>`, not `<div>`. It is built on Emotion, and
331
+ the first host it emits is Emotion's injected style tag — so this is the
332
+ CSS-in-JS failure rather than the component failure.
333
+ - `formik` reports `<input>` when your `<Form>` has a `<Field>` in it and
334
+ `<form>` when it does not, because the reconciler completes children
335
+ first.
336
+ - `react-router-dom`'s `<Link>` with a bare string child hits the raw-text
337
+ error first: `react-x11: raw text "go" must be wrapped in a <text> element
338
+ (or be the string child of an SVG <text>).`
339
+
340
+ #### Charting, without recharts {#charting}
341
+
342
+ There is no charting entry in the compatibility table, but the substitute
343
+ for `recharts` is small: compute with `d3-scale` + `d3-shape` and emit the
344
+ path into react-x11's own `<svg>` host.
345
+
346
+ ```jsx
347
+ import { scaleLinear } from 'd3-scale';
348
+ import { line } from 'd3-shape';
349
+
350
+ const data = [
351
+ [0, 3],
352
+ [1, 7],
353
+ [2, 4],
354
+ [3, 9],
355
+ [4, 6],
356
+ ];
357
+ const x = scaleLinear().domain([0, 4]).range([0, 200]);
358
+ const y = scaleLinear().domain([0, 10]).range([100, 0]);
359
+ const d = line()
360
+ .x((p) => x(p[0]))
361
+ .y((p) => y(p[1]))(data);
362
+
363
+ <svg style={{ width: 200, height: 100 }} viewBox="0 0 200 100">
364
+ <path d={d} stroke="#89b4fa" fill="none" strokeWidth={2} />
365
+ </svg>;
366
+ ```
367
+
368
+ The `<canvas>` host with an `onDraw` handler is the other option, and the
369
+ better one for dense plots.
370
+
371
+ ### First interaction
372
+
373
+ These mount without a single error message. The first sign of trouble is a
374
+ keystroke.
375
+
376
+ **Two entries left this table in 2.0.0.** `downshift` (a TypeError on the
377
+ first keystroke) and `formik`'s `getFieldProps` spread (silent, no error,
378
+ the field simply never updated) both had one cause: `<textinput>` called
379
+ `onChange(newString)` rather than `onChange(event)`. It now passes a
380
+ synthetic event, and both work — see
381
+ [forms](ecosystem/forms.md). They are recorded here because the symptoms are
382
+ what a 1.x user searches for.
383
+
384
+ Nothing currently fails on first interaction. Two shapes to know about
385
+ anyway, because they are the ones this class of bug takes here:
386
+
387
+ - A handler that reads `event.nativeEvent.<something>` **unguarded** works
388
+ for a keystroke and throws for a paste or an undo, where there is no X
389
+ event to report. downshift reads
390
+ `event.nativeEvent.preventDownshiftDefault` this way; it is safe only
391
+ because it also checks `hasOwnProperty('nativeEvent')` first and the
392
+ property is always present.
393
+ - A library that writes through a ref — `ref.value = ''` — needs the node to
394
+ have a value **setter**. `<textinput>` does; most nodes do not, and the
395
+ failure is a TypeError thrown from inside the commit phase, which takes the
396
+ render down rather than one handler.
397
+
398
+ ### Silent failures
399
+
400
+ No error is ever printed. Read this table before you spend an afternoon.
401
+
402
+ | Package | What happens | Fix |
403
+ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
404
+ | `swr` | `(silent) data never refetches when the X11 window regains focus — no error, no warning, revalidateOnFocus just never fires` | define `globalThis.window` before importing swr, or use `@tanstack/react-query` — [data fetching](ecosystem/data-fetching.md#swr) |
405
+ | `@tanstack/react-virtual` (with `react-dom` installed) | `(silent) with react-dom installed the import resolves and the list renders, but the flushSync re-measure applies to react-dom's reconciler, not react-x11's` | `@tanstack/virtual-core` — [layout](ecosystem/layout.md#tanstack-virtual) |
406
+ | `react-scan` | `(silent) no error, no output — onRender never fires and getReport() stays empty` | React's own `<Profiler>` (verified working), or why-did-you-render |
407
+ | `@radix-ui/react-dialog` (`Dialog.Portal`) | renders nothing; the SSR guard finds no `document` | react-x11's built-in `Dialog` |
408
+ | `styled-components` (`styled('box')`) | renders with no error and hands the box a single `className` prop, which react-x11 ignores — the CSS goes nowhere | `createStyles` + `ThemeProvider` |
409
+ | `tailwindcss/colors` (raw values) | v4 ships `oklch()` strings; ntk's parser returns null and paints nothing | convert with `culori` — [theming](ecosystem/theming.md#tailwind-colors) |
410
+
411
+ The swr one is the most expensive, so here it is in full. `useSWR` fetches
412
+ on mount, caches and dedupes correctly. What never happens is focus
413
+ revalidation, because swr computes its environment once at import:
414
+
415
+ ```js
416
+ // swr/dist/_internal/config-context-*.mjs
417
+ const IS_SERVER = !isWindowDefined || isLegacyDeno;
418
+ ```
419
+
420
+ In Node there is no global `window`, so `initCache`'s `if (!IS_SERVER)`
421
+ branch never runs and neither `initFocus` nor `initReconnect` is ever
422
+ called — swr registers zero listeners. The documented React Native escape
423
+ hatch does not help either: a custom `initFocus` passed through
424
+ `<SWRConfig value={{ provider, initFocus }}>` is never called, because the
425
+ guard sits above it. The workaround, and its one gotcha, are on the
426
+ [data fetching](ecosystem/data-fetching.md#swr) page.
427
+
428
+ The `@tanstack/react-virtual` entry is subtle in a different way. `react-dom`
429
+ is a _required_ peer, so a default `npm i @tanstack/react-virtual` installs
430
+ it and the import-time error above never appears. `react-dom@19`'s
431
+ `flushSync()` called outside any react-dom root runs the callback and does
432
+ not throw — so nothing warns you that the synchronous flush the virtualizer
433
+ depends on is happening in the wrong renderer.
434
+
435
+ ## Adding a library that is not listed
436
+
437
+ Run the [30-second test](#the-30-second-test) first; it settles most cases
438
+ without an install. If it passes, the fastest real check is a render:
439
+
440
+ ```jsx
441
+ // probe.jsx — needs a display: run under $DISPLAY, or `xvfb-run -a`
442
+ import { createRoot } from 'react-x11';
443
+ import { TheThing } from 'the-package';
444
+
445
+ (await createRoot()).render(
446
+ <window width={300} height={200} title="probe">
447
+ <box style={{ flexGrow: 1 }}>
448
+ <TheThing />
449
+ </box>
450
+ </window>,
451
+ );
452
+ ```
453
+
454
+ Four outcomes, and each one tells you where to look:
455
+
456
+ - **`react-x11: unknown element type <…>`** — the library renders DOM hosts.
457
+ Look for a headless core published as a separate package; that is what
458
+ rescued TanStack Virtual, Floating UI and react-spring.
459
+ - **`react-x11: <… width=…> is a style property`** — it got as far as a real
460
+ react-x11 host and then passed a flat style prop. Usually fixable by
461
+ wrapping the component rather than replacing it.
462
+ - **`ReferenceError: document is not defined`** (or `window`, or
463
+ `HTMLElement`) — a DOM global at module or mount time. Check whether the
464
+ library has a documented environment-override seam, the way
465
+ `@tanstack/react-query`'s `environmentManager` does.
466
+ - **It renders.** Then type into it, click it, and resize the window. The
467
+ event-shape and silent classes only show up here.
468
+
469
+ If you establish a result — either direction — open an issue on
470
+ [the react-x11 repository](https://github.com/sidorares/react-x11/issues)
471
+ with the package name, its exact version, the code you ran, and the verbatim
472
+ output. Negative results are as useful as positive ones on this page; most
473
+ of what is above started as somebody's wasted afternoon.