Skip to content
playhtml

Using React

playhtml ships a first-class React package. This page is an orientation; the rest of the docs show you the React form of each concept inline, next to the vanilla HTML form, so you can pick the shape that fits your codebase.

npm install @playhtml/react

Compatible with React 16.8 and above, including React 17, 18, and 19.

Every React app using playhtml needs a single <PlayProvider> near the root. It initializes the client and opens the connection. Anything you’d pass to playhtml.init() goes in initOptions.

import { PlayProvider } from "@playhtml/react";

export default function App() {
  return (
    <PlayProvider
      initOptions={{
        // room: "my-room",
        // cursors: { enabled: true },
      }}
    >
      <YourApp />
    </PlayProvider>
  );
}

A new page / route in a multi-page app needs its own provider only if it’s a separate React root; for most apps PlayProvider wraps your entire tree once.

Almost every React element in playhtml is built from one of these.

Wrap a component to give it live, shared data plus a setData callback.

import { withSharedState } from "@playhtml/react";

export const ToggleSquare = withSharedState(
  { defaultData: { on: false } },
  ({ data, setData }) => (
    <div
      style={{ background: data.on ? "green" : "red", width: 200, height: 200 }}
      onClick={() => setData({ on: !data.on })}
    />
  ),
);

Pass a callback instead of an object if the default data depends on props:

export const ReactionView = withSharedState(
  ({ reaction: { count } }) => ({ defaultData: { count } }),
  ({ data, setData }, { reaction: { emoji } }) => (
    <button onClick={() => setData({ count: data.count + 1 })}>
      {emoji} {data.count}
    </button>
  ),
);

Add myDefaultAwareness to the config to get presence-style ephemeral per-user data alongside your persistent data.

<CanPlayElement> — for when you need JSX children, not a wrapper

Section titled “<CanPlayElement> — for when you need JSX children, not a wrapper”

Same idea, different shape. Useful when the thing you’re wrapping is a specific DOM element you want to keep named, or when you want access to ref.

import { CanPlayElement } from "@playhtml/react";
import { TagType } from "@playhtml/common";

<CanPlayElement
  tagInfo={[TagType.CanToggle]}
  id="my-lamp"
  defaultData={{ on: false }}
>
  {({ data, setData }) => (
    <button onClick={() => setData({ on: !data.on })}>
      {data.on ? "on" : "off"}
    </button>
  )}
</CanPlayElement>;

Every concept page shows both the vanilla and React forms. Pick whichever matches your codebase:

For the full React component/hook signatures and types, see React API.