Skip to main content

react-x11 documentation

  • elements.md — the host elements: <window>, <popup>, <box>, <text>, <textinput>, <textarea>, <image>, <canvas>, <svg>, <foreign>, their props and refs — including selecting read-only text with selectable.
  • styling.md — the style prop: layout and paint properties, :hover/:focus/:active/:disabled blocks, transitions, theme tokens, window size queries, createStyles, and a font file of your ownopenFont/loadFont/useFont.
  • components.md — widget components built on the primitives: theming, the basic controls (Button, Checkbox, Radio/RadioGroup, Switch, ProgressBar), Select, Slider, Tooltip, Dialog, MenuBar/ContextMenu, Tabs, Table, SplitPane, and the useAnchor popup placement hook.
  • globalmenu.md — handing a window's menu bar to the desktop's panel where there is one, with no configuration: the dbusmenu item vocabulary MenuBar shares with it, why detection means "a panel is running" rather than "one could be started", the diff that decides between patching properties and invalidating a layout, and the menu host in scripts/ for seeing it work without such a desktop.
  • events.md — the synthetic event system: dispatch phases, event object shape, focus, cursors, default actions.
  • accessibility.md — screen readers see react-x11 apps: the built-in AT-SPI2 bridge, the standard role/aria-* props on every element, what the defaults already say, what each widget announces, announce(), driving controls from assistive technology, the no-bus compatibility ladder, and how to test all of it without a desktop. The design record behind it — no mirror tree, the event pipeline, what was rejected — is architecture/accessibility.md.
  • react-features.md — what your React knowledge buys you here and where a DOM habit breaks: useLayoutEffect vs useEffect, measuring a node without getBoundingClientRect(), what a ref hands back, <popup> instead of createPortal, where to put an error boundary so the window survives, and which React APIs are simply not available.
  • drag-and-drop.md — accepting drops and starting drags, over XDND and in-app: dropAccept matching, the payload (e.files, e.getData, live e.items), dragData and lazy payloads, the useDropTarget/useDragSource hooks, drag previews, what GTK and Firefox actually offer, and how to drive a drag in a test.
  • clipboard.md — copy and paste beyond the built-in text controls: useClipboard(), the CLIPBOARD/PRIMARY split, offering several flavours of one payload, the text/files/uris groups shared with drag and drop, watch(), and why a copy carries a timestamp.
  • typescript.md — the bundled types: one tsconfig option, why JSX comes from react-x11/jsx-runtime rather than an augmentation, and how the declarations are kept from drifting.
  • extending.mdregisterElement() and the subpath exports: adding a host element from outside the package, the node contract, the two definition fields that fail far from their cause, and the damage seam an element that draws a whole scene reads and claims.
  • embedding.md<foreign>: another application's window inside yours. XEmbed and the plain-reparent path most clients actually want, why unmount hands the window back rather than destroying it, and the rule that keeps an app's chords from being swallowed by the client.
  • testing.mdreact-x11/test: render, query, drive and assert on real pixels, against a real X server in your test process. No display, no xvfb.
  • desktop.md — what an app tells the desktop about itself beyond drawing. Startup notification: why the launcher's busy cursor spins for 15 seconds without it, when an app counts as "started", and the seams for saying otherwise.
  • dbus.md — the bus every desktop-integration feature sits on: useSessionBus() / useSystemBus() with nothing to wrap, the imperative sessionBus() pair underneath, why one process is one connection and one identity, and why "there is no bus" is a first-class configuration rather than an error.
  • uri-schemes.md — being the app a myapp://… link opens: registerApplication() and the two dispatch paths a real desktop uses (only one of which is D-Bus), why a second copy of the app has to forward and exit, useAppOpen(), and the timestamp without which the window does not actually come forward. Also: why a loopback port is the better answer for a login, and the .desktop half that is an install step rather than code.
  • filedialog.md — open, save and pick a folder: useFileDialog(), the three-rung ladder (the desktop's own portal, osascript on macOS, a browser react-x11 draws itself), why cancelling is null rather than a throw, and the places the backends genuinely differ.
  • eyedropper.md — sample a colour from the screen: useEyedropper()/pickScreenColor(), the two-rung ladder (the portal's own picker, a crosshair grab on plain X11), the interface version property that gates the portal rung where hasService() cannot, and why the grab's whole lifecycle — including the <popup grab> it displaces — belongs to core.
  • appearance.md — light or dark, the accent colour, contrast and reduced motion: useSystemAppearance(), <ThemeProvider dark> for apps that only want to follow the desktop, why the answer is remembered on disk so the first frame is not a flash, and what each rung of the ladder can actually answer.
  • scale.md — HiDPI: every length you write is a logical pixel, createRoot({ scale: 'auto' }) resolves how many device pixels one is worth (environment → XSETTINGS/Xft.dpi → audited RandR millimetres → the resolution class), useScale(), per-monitor answers on useScreens(), and why a virtual machine's EDID needs the audit.
  • system.md — the machine around the app: useScreens() for the monitor layout, useWindowState() for what the window manager actually did, useIdle()/useKeepAwake(), useKeyboardState() for Caps Lock and the live layout, useDesktopSettings() for the timings the built-in controls already follow, and useLocale(). Also what each one answers on a display with the extension missing, and why obscured is always false under a compositor.
  • remote.md — the flagship case: running the app on one machine and drawing to a display on another. ssh -X vs -Y, what the protocol costs on a link, the other X servers, and why Xwayland works where native Wayland structurally cannot.
  • security.md — the threat model, plainly: X11 has no isolation between clients, $XAUTHORITY is a password, and what react-x11 does and does not defend against.
  • packaging.md — four ways to ship an app, with the two esbuild flags that are load-bearing and the one tier that does not work.
  • devtools.md — React DevTools integration and other debugging aids.
  • debugging.md — runtime diagnostics: protocol tracing (REACT_X11_TRACE, startTrace()), repaint flashing and full-repaint warnings (REACT_X11_DEBUG_PAINT), invalidation reasons.
  • click-to-component.md — Alt+Click a rendered element to open its JSX source line in your editor.
  • ecosystem.md — which npm packages work with react-x11 and which do not: the rule that decides it, a compatibility table across 12 categories, and a register of the verbatim errors the incompatible ones produce. Per-category pages: state, data fetching, forms, icons, theming, animation, headless components, layout, routing, i18n, testing, dev tooling.

Entry points

import { createRoot, Select } from 'react-x11';

await createRoot(options?){ app, render(element), unmount() }

The entry point. With no options it connects to the X server named by $DISPLAY; the returned app is the ntk App, one X connection.

Every root without app opens its own connection and owns it, so two roots are two independent trees, and await root.unmount() closes what it opened — without which the socket stays up and the process does not exit. A root given an app borrows it and never closes it: that connection belongs to whoever made it.

option
display':1', 'host:0.0', a socket path. Default $DISPLAY
apprender into a connection you already have
streaman already-connected duplex stream
fontSourcepluggable system-font lookup — ntk's docs/fonts.md
glxVisualvisual id for getContext('opengl')
onXErrorX protocol errors nothing claimed. Default warns
onUncaughtError onCaughtError onRecoverableError(error, errorInfo); default logs the component stack
onDisconnect(reason, err)the connection ended — 'closed' or 'error'

display, stream, fontSource, glxVisual and onXError go straight to ntk's createClient. Anything else it understands, build the client yourself and pass it as app — which is also how the hermetic tests drive the renderer against node-x11's in-process X server:

import xserver from 'x11/lib/xserver/index.js';

const server = xserver.createServer({ width: 640, height: 480 });
const [serverEnd, clientEnd] = xserver.createStreamPair();
server.addClientStream(serverEnd);
const root = await createRoot({ stream: clientEnd }); // no $DISPLAY needed

onUncaughtError covers one channel React does not: a throw from an event handler, which no error boundary can catch because the handler ran from an X event rather than a render. See events.md.

onDisconnect fires when the connection ends without being asked to — server exit, ssh drop, kill — and not for one this root closed. It invites a reconnect loop, so: a reconnect is not a reconnect. Every window id, pixmap, glyph set and font is invalidated with the connection. Tear the root down and build a new one; nothing survives, and react-x11 promises nothing more than telling you it happened.

root.render(element, callback?) flushes the mount or update synchronously (updateContainerSync + flushSyncWork); painting happens a frame later on ntk's frame clock.

Environment variables

variableeffect
DISPLAYX server to connect to (standard X11)
REACT_X11_DEVTOOLS=1connect to a running react-devtools (see devtools.md)
REACT_X11_DEVTOOLS_HOSTdevtools host (default localhost)
REACT_X11_DEVTOOLS_PORTdevtools port (default 8097)
REACT_X11_DEBUG_LAYOUT=1outline every laid-out node, color-coded by tree depth
REACT_X11_CLICK_TO_COMPONENT=1Alt+Click opens the clicked element's source, using cursor (see click-to-component.md)
REACT_X11_EDITOReditor CLI for click-to-component — setting this alone also enables it
  • gl.md — the two 3D backends: glPolicy, what each can draw, <shaderMaterial> — GLSL with three.js's names — and <effectComposer> post-processing, both on the direct one
  • glx.md — how the 3D scene works over indirect GLX: what the protocol encodes, why display lists are mandatory, and what can never work