demo/src/react-host.tsx

import { type ComponentType, createElement } from "react";
import { createRoot } from "react-dom/client";
import { getOwner, onCleanup, runWithOwner } from "solid-js";
import { DemoErrorBoundary } from "./react/_error-boundary.tsx";

/**
 * Mounts a React component inside the Solid app. Each side-by-side row renders
 * the official `@base-ui/react` demo through one of these hosts, while the Solid
 * column renders our `src/` library directly.
 *
 * The mount happens in the `ref` callback (which Solid runs as the host `<div>`
 * is created); `createRoot` accepts a not-yet-attached node, so React's subtree
 * travels with the div when Solid inserts it. The demo is wrapped in a React
 * error boundary so one broken demo doesn't tear down its root.
 */
export function ReactHost(props: { component: ComponentType }) {
	// Ref callbacks run in an unowned scope, so register cleanup against the
	// component's owner explicitly — otherwise `onCleanup` is a no-op
	// (`[NO_OWNER_CLEANUP]`) and the React roots never unmount.
	const owner = getOwner();
	return (
		<div
			ref={(node) => {
				const root = createRoot(node);
				root.render(
					createElement(
						DemoErrorBoundary,
						null,
						createElement(props.component),
					),
				);
				// Unmount on a microtask to dodge React's "unmount during render"
				// warning when Solid disposes this row.
				runWithOwner(owner, () => {
					onCleanup(() => queueMicrotask(() => root.unmount()));
				});
			}}
		/>
	);
}