Skip to content

Getting started

playhtml turns any HTML element into a live, collaborative one with a single attribute. This guide gets you from empty page to a working shared toggle.

Pick your setup:

There are two paths. Pick the one that matches your project.

Path A: drop-in script (no build, no module syntax)

Section titled “Path A: drop-in script (no build, no module syntax)”

The fastest path, with no import and no playhtml.init() call. Add the script tag at the end of your <body>, after your interactive elements:

<body>
  <button id="my-lamp" can-toggle>lamp</button>

  <script type="module" src="https://unpkg.com/playhtml/dist/init.es.js"></script>
</body>

Give every interactive element a unique id. That’s how state is keyed and synced across everyone on the page.

Why end-of-body? The drop-in script runs playhtml.init() immediately, and init() scans the DOM for capability attributes (can-toggle, can-move, etc.) when it runs. Place it after the elements it should find.

Want options like cursors? Use Path B. The drop-in script calls playhtml.init({}) with no options.

Path B: import + manual init (when you need options or a bundler)

Section titled “Path B: import + manual init (when you need options or a bundler)”

Reach for this when you want to pass options to init() (cursors, custom rooms, etc.) or you’re already using a bundler.

From a CDN, no build step: put the script at the end of <body> so the elements exist when init() runs.

<body>
  <button id="my-lamp" can-toggle>lamp</button>

  <script type="module">
    import { playhtml } from "https://unpkg.com/playhtml";
    playhtml.init({ cursors: { enabled: true } });
  </script>
</body>

From npm with a bundler:

npm install playhtml
import { playhtml } from "playhtml";
playhtml.init({ cursors: { enabled: true } });

Bundlers usually handle script placement for you; just make sure init() runs after your interactive elements are in the DOM (e.g. on DOMContentLoaded or after your framework mounts).

Either way, give every interactive element a unique id. That’s how state is keyed and synced across everyone on the page.

This toggle is shared with everyone reading this page right now. Click it to see it update for all readers.

  • Core concepts: the four kinds of shared state (element data, page data, presence, events) and when to use each.
  • Using React: if your app is a React app, start here; concept pages show React inline.
  • Capabilities: every built-in can-* attribute with live demos.
  • Data essentials: shape, update, and clean up defaultData.
  • Presence & cursors: multiplayer cursors and ephemeral per-user state.
  • Shared elements: cross-page and cross-domain state.
  • Building with AI: Claude Code plugin + a prompt template for any LLM.
  • API reference: the full playhtml.init() options table and React API types.