Loading state
When a page opens, playhtml has to fetch the room’s shared data before elements can show it. Until then, an element would show its default data (a counter at 0, a toggle off). playhtml marks elements as loading during that wait so people don’t act on a default that’s about to change.
Loading lasts until the room syncs, usually well under a second. It starts again whenever navigation moves to a different room.
What playhtml does while loading
Section titled “What playhtml does while loading”An element with loading treatment gets these until its room’s data is in:
- The
playhtml-loadingclass. aria-busy="true", so screen readers know the content is about to change.pointer-events: none, so nobody clicks or drags an element that’s about to change.
How the element looks depends on its kind:
- Built-in elements (
can-move,can-toggle,can-spin, and the rest) fade in and out gently. - Custom elements (
can-play,withSharedState) get no loading treatment by default, because playhtml can’t know what a sensible placeholder is for your element. They are clickable right away. Setting any of the options below turns loading treatment on.
Choosing how an element looks while loading
Section titled “Choosing how an element looks while loading”Three attributes control it. They work on any playhtml element.
| Attribute | Values | What it does |
|---|---|---|
loading-behavior | animate, hidden, none | animate fades the element in and out. hidden keeps it invisible until the data is in. none turns all loading treatment off: no class, no aria-busy, and the element looks normal. |
loading-style | breathing, pulse, fade, none | Which look to use. breathing is the gentle fade the built-in elements use. pulse is a faster blink. fade holds the element at low opacity. none shows the element as normal, but it keeps the playhtml-loading class and aria-busy, so you can style it yourself. |
loading-class | any class name | Adds your own class while loading, so you can style the placeholder yourself. |
Try each option below. The button uses playhtml’s real loading styles. Switch between loading and loaded, and try clicking it while it loads.
Each choice trades something:
animateshows the default value while loading, but makes it clear the value isn’t final.hiddennever shows the default. The element still takes up its space, so the page has a blank gap until the data is in.noneshows the element as normal right away. People see the default value, and clicking it does nothing until the data is in.
<!-- Invisible until its data is in, so nobody sees the default -->
<div id="scoreboard" can-play loading-behavior="hidden"></div>
<!-- A toggle that pulses instead of breathing -->
<button id="lamp" can-toggle loading-style="pulse">lamp</button>
<!-- Your own placeholder style -->
<div id="guestbook" can-play loading-class="is-placeholder"></div>
<style>
.is-placeholder {
background: repeating-linear-gradient(45deg, #eee 0 8px, #f6f6f6 8px 16px);
}
</style> Pass the same options as loading: behavior, style, and customClass.
import { withSharedState } from "@playhtml/react";
export const Scoreboard = withSharedState(
{
defaultData: { score: 0 },
loading: { behavior: "hidden" },
},
({ data }) => <div>{data.score}</div>,
);On a CanPlayElement, use the loading prop: loading={{ behavior: "animate", style: "pulse" }}.
You can also style loading elements with plain CSS. Target .playhtml-loading or [aria-busy="true"]:
.playhtml-loading {
filter: grayscale(1);
}
Showing your own loading UI
Section titled “Showing your own loading UI”To render something different while the page loads, such as a “connecting…” line or a skeleton, read playhtml’s loading state directly.
playhtml.isLoading is true until the room’s data is in. A playhtml:loading-change event fires on document each time it changes:
<p id="status">connecting…</p>
<script type="module">
import { playhtml } from "playhtml";
const status = document.getElementById("status");
const render = () => {
status.hidden = !playhtml.isLoading;
};
document.addEventListener("playhtml:loading-change", render);
playhtml.init();
render();
</script>To run code once the data is in, wait for playhtml.ready:
await playhtml.ready;
console.log("data is in"); usePlayContext().isLoading is true until the room’s data is in, and the component re-renders when it changes:
import { usePlayContext } from "@playhtml/react";
export function ConnectionStatus() {
const { isLoading } = usePlayContext();
return isLoading ? <p>connecting…</p> : null;
}Hooks that read shared data, like usePageData, return the default until loading ends. Check isLoading before acting on that value, for example before counting a visit.
When loading starts again
Section titled “When loading starts again”Loading isn’t only at page open. When client-side navigation moves to a different room, the new room’s data has to load too:
isLoadingturnstrueagain,playhtml:loading-changefires, and elements get their loading treatment.playhtml.readybecomes a new promise for the new room. Readplayhtml.readyagain instead of reusing one you saved earlier.
Navigating within the same room doesn’t trigger loading. See Navigation & SPAs for how rooms are picked.