feat(site): tailwind demo variants for util and controller references

Adds react/tailwind and html/tailwind demo variants for create-player,
html-create-player, player-controller, render-element, use-button,
use-player, and use-store, gated on the style preference via StyleCase.

Refs #840

https://claude.ai/code/session_01LUcwUd8E4Ekakp7NTFb6c5
This commit is contained in:
Claude
2026-06-11 22:52:48 +00:00
parent 48e47187be
commit 253beecbf9
20 changed files with 636 additions and 0 deletions
@@ -0,0 +1,40 @@
import { createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({
features: videoFeatures,
});
function Controls() {
const store = Player.usePlayer();
const paused = Player.usePlayer((s) => s.paused);
return (
<div className="absolute bottom-2.5 left-2.5">
<button
type="button"
className="cursor-pointer rounded-full border border-white/30 bg-white/70 px-5 py-2 text-black backdrop-blur-[10px]"
onClick={() => (paused ? store.play() : store.pause())}
>
{paused ? 'Play' : 'Pause'}
</button>
</div>
);
}
export default function BasicUsage() {
return (
<Player.Provider>
<Player.Container className="relative">
<Video
className="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoPlay
muted
playsInline
/>
<Controls />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,9 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
---
<HtmlDemo html={html} />
<script>
import "./BasicUsage.ts";
</script>
@@ -0,0 +1,17 @@
<demo-video-player class="relative">
<media-container>
<video
class="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
></video>
<demo-play-toggle
class="absolute bottom-2.5 left-2.5 cursor-pointer rounded-full border border-white/30 bg-white/70 px-5 py-2 text-black backdrop-blur-[10px]"
>
<span class="hidden in-data-paused:inline">Play</span>
<span class="hidden not-in-data-paused:inline">Pause</span>
</demo-play-toggle>
</media-container>
</demo-video-player>
@@ -0,0 +1,59 @@
import {
applyElementProps,
applyStateDataAttrs,
createButton,
createPlayer,
MediaElement,
PlayerController,
selectPlayback,
} from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';
import '@videojs/html/media/container';
const { ProviderMixin, context } = createPlayer({
features: videoFeatures,
});
class VideoPlayer extends ProviderMixin(MediaElement) {
static readonly tagName = 'demo-video-player';
}
class PlayToggle extends MediaElement {
static readonly tagName = 'demo-play-toggle';
readonly #player = new PlayerController(this, context, selectPlayback);
#disconnect: AbortController | null = null;
override connectedCallback(): void {
super.connectedCallback();
this.#disconnect = new AbortController();
const buttonProps = createButton({
onActivate: () => {
const state = this.#player.value;
if (!state) return;
state.paused ? state.play() : state.pause();
},
isDisabled: () => !this.#player.value,
});
applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
}
override disconnectedCallback(): void {
super.disconnectedCallback();
this.#disconnect?.abort();
this.#disconnect = null;
}
protected override update(changed: Map<string, unknown>): void {
super.update(changed);
const state = this.#player.value;
if (!state) return;
applyStateDataAttrs(this, state, { paused: 'data-paused', ended: 'data-ended' });
}
}
customElements.define(VideoPlayer.tagName, VideoPlayer);
customElements.define(PlayToggle.tagName, PlayToggle);
@@ -0,0 +1,9 @@
---
import HtmlDemo from '@/components/docs/demos/HtmlDemo.astro';
import html from './BasicUsage.html?raw';
---
<HtmlDemo html={html} />
<script>
import "./BasicUsage.ts";
</script>
@@ -0,0 +1,36 @@
<demo-ctrl-player class="relative">
<media-container>
<video
class="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoplay
muted
playsinline
></video>
<div class="flex items-center gap-4 border-t border-black/10 bg-black/5 p-3">
<demo-ctrl-actions class="flex gap-1.5">
<button
type="button"
class="play cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
>
Play
</button>
<button
type="button"
class="pause cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
>
Pause
</button>
<button
type="button"
class="volume cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
>
50% Volume
</button>
</demo-ctrl-actions>
<demo-ctrl-state class="text-[13px] text-gray-700 tabular-nums">
<span class="text">Paused: Yes | Time: 0.0s | Volume: 100%</span>
</demo-ctrl-state>
</div>
</media-container>
</demo-ctrl-player>
@@ -0,0 +1,78 @@
import {
applyElementProps,
createButton,
createPlayer,
MediaElement,
PlayerController,
selectPlayback,
selectTime,
selectVolume,
} from '@videojs/html';
import { videoFeatures } from '@videojs/html/video';
import '@videojs/html/media/container';
const { ProviderMixin, context } = createPlayer({
features: videoFeatures,
});
class DemoPlayer extends ProviderMixin(MediaElement) {
static readonly tagName = 'demo-ctrl-player';
}
class PlayerActions extends MediaElement {
static readonly tagName = 'demo-ctrl-actions';
readonly #player = new PlayerController(this, context);
#disconnect: AbortController | null = null;
override connectedCallback(): void {
super.connectedCallback();
this.#disconnect = new AbortController();
const signal = this.#disconnect.signal;
const playBtn = this.querySelector<HTMLButtonElement>('.play')!;
const pauseBtn = this.querySelector<HTMLButtonElement>('.pause')!;
const volumeBtn = this.querySelector<HTMLButtonElement>('.volume')!;
const bind = (el: HTMLElement, action: () => void) => {
const props = createButton({ onActivate: action, isDisabled: () => !this.#player.value });
applyElementProps(el, props, { signal });
};
bind(playBtn, () => this.#player.value?.play());
bind(pauseBtn, () => this.#player.value?.pause());
bind(volumeBtn, () => this.#player.value?.setVolume(0.5));
}
override disconnectedCallback(): void {
super.disconnectedCallback();
this.#disconnect?.abort();
this.#disconnect = null;
}
}
class PlayerState extends MediaElement {
static readonly tagName = 'demo-ctrl-state';
readonly #playback = new PlayerController(this, context, selectPlayback);
readonly #time = new PlayerController(this, context, selectTime);
readonly #volume = new PlayerController(this, context, selectVolume);
protected override update(changed: Map<string, unknown>): void {
super.update(changed);
const playback = this.#playback.value;
const time = this.#time.value;
const volume = this.#volume.value;
if (!playback) return;
const el = this.querySelector('.text');
if (el) {
el.textContent = `Paused: ${playback.paused ? 'Yes' : 'No'} | Time: ${(time?.currentTime ?? 0).toFixed(1)}s | Volume: ${Math.round((volume?.volume ?? 0) * 100)}%`;
}
}
}
customElements.define(DemoPlayer.tagName, DemoPlayer);
customElements.define(PlayerActions.tagName, PlayerActions);
customElements.define(PlayerState.tagName, PlayerState);
@@ -0,0 +1,68 @@
import { renderElement } from '@videojs/react';
import { type ReactNode, useState } from 'react';
interface TagState {
active: boolean;
}
function Tag({
className,
style,
render,
active,
children,
}: renderElement.ComponentProps<TagState> & { active: boolean; children?: ReactNode }) {
const state: TagState = { active };
return renderElement(
'span',
{ className, style, render },
{
state,
props: { children },
stateAttrMap: { active: 'data-active' },
}
);
}
export default function BasicUsage() {
const [active, setActive] = useState(false);
const className = (state: TagState) =>
state.active
? 'inline-flex items-center rounded-full bg-blue-500 px-3 py-1.5 text-white transition-all duration-200'
: 'inline-flex items-center rounded-full bg-gray-200 px-3 py-1.5 text-gray-700 transition-all duration-200';
const style = (state: TagState) => ({
fontSize: state.active ? '1.125rem' : '0.875rem',
});
return (
<div className="flex flex-col gap-4 p-4">
<button
type="button"
className="cursor-pointer self-start rounded-md border border-gray-300 bg-neutral-100 px-4 py-1.5"
onClick={() => setActive((prev) => !prev)}
>
{active ? 'Deactivate' : 'Activate'}
</button>
<div className="flex flex-wrap gap-3">
<Tag active={active} className={className} style={style}>
Default &lt;span&gt;
</Tag>
<Tag active={active} className={className} style={style} render={<strong />}>
Element &lt;strong&gt;
</Tag>
<Tag
active={active}
className={className}
style={style}
render={(props, state) => <em {...props}>{state.active ? 'Active!' : 'Inactive'}</em>}
/>
</div>
</div>
);
}
@@ -0,0 +1,31 @@
import { useButton } from '@videojs/react';
import type { Ref } from 'react';
import { useState } from 'react';
export default function BasicUsage() {
const [count, setCount] = useState(0);
const [disabled, setDisabled] = useState(false);
const { getButtonProps, buttonRef } = useButton({
displayName: 'ActivateButton',
onActivate: () => setCount((c) => c + 1),
isDisabled: () => disabled,
});
return (
<div className="flex flex-col gap-3 p-4">
<button
ref={buttonRef as Ref<HTMLButtonElement>}
{...getButtonProps()}
className="cursor-pointer self-start rounded-md border border-gray-300 bg-neutral-100 px-5 py-2 tabular-nums transition-opacity duration-200 disabled:cursor-not-allowed disabled:opacity-50"
disabled={disabled}
>
Activated {count} times
</button>
<label className="flex items-center gap-1.5 text-sm text-gray-500">
<input type="checkbox" checked={disabled} onChange={(e) => setDisabled(e.target.checked)} />
Disabled
</label>
</div>
);
}
@@ -0,0 +1,47 @@
import { createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({
features: videoFeatures,
});
function StateDisplay() {
const state = Player.usePlayer((s) => ({
paused: s.paused,
currentTime: s.currentTime,
duration: s.duration,
}));
return (
<dl className="m-0 flex gap-4 border-t border-black/10 bg-black/5 p-3 text-[13px]">
<div className="flex gap-2">
<dt className="text-gray-500">Paused</dt>
<dd className="m-0 tabular-nums">{String(state.paused)}</dd>
</div>
<div className="flex gap-2">
<dt className="text-gray-500">Time</dt>
<dd className="m-0 tabular-nums">
{state.currentTime.toFixed(1)}s / {state.duration.toFixed(1)}s
</dd>
</div>
</dl>
);
}
export default function Selector() {
return (
<Player.Provider>
<Player.Container className="relative">
<Video
className="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<StateDisplay />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,47 @@
import { createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({
features: videoFeatures,
});
function Controls() {
const store = Player.usePlayer();
return (
<div className="flex gap-1.5 border-t border-black/10 bg-black/5 p-3">
<button
type="button"
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
onClick={() => store.play()}
>
Play
</button>
<button
type="button"
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
onClick={() => store.pause()}
>
Pause
</button>
</div>
);
}
export default function StoreAccess() {
return (
<Player.Provider>
<Player.Container className="relative">
<Video
className="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<Controls />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,45 @@
import { createPlayer, useStore } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({
features: videoFeatures,
});
function DerivedState() {
const store = Player.usePlayer();
const derived = useStore(store, (s) => ({
remaining: s.duration - s.currentTime,
progress: s.duration > 0 ? (s.currentTime / s.duration) * 100 : 0,
}));
return (
<dl className="m-0 flex gap-4 border-t border-black/10 bg-black/5 p-3 text-[13px]">
<div className="flex gap-2">
<dt className="text-gray-500">Remaining</dt>
<dd className="m-0 tabular-nums">{derived.remaining.toFixed(1)}s</dd>
</div>
<div className="flex gap-2">
<dt className="text-gray-500">Progress</dt>
<dd className="m-0 tabular-nums">{derived.progress.toFixed(1)}%</dd>
</div>
</dl>
);
}
export default function Selector() {
return (
<Player.Provider>
<Player.Container className="relative">
<Video
className="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<DerivedState />
</Player.Container>
</Player.Provider>
);
}
@@ -0,0 +1,48 @@
import { createPlayer, useStore } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const Player = createPlayer({
features: videoFeatures,
});
function SeekControls() {
const store = Player.usePlayer();
const s = useStore(store);
return (
<div className="flex gap-1.5 border-t border-black/10 bg-black/5 p-3">
<button
type="button"
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
onClick={() => s.seek(0)}
>
Go to start
</button>
<button
type="button"
className="cursor-pointer rounded-md border border-gray-300 bg-white px-3 py-1 text-[13px]"
onClick={() => s.seek(s.state.duration / 2)}
>
Go to middle
</button>
</div>
);
}
export default function StoreAccess() {
return (
<Player.Provider>
<Player.Container className="relative">
<Video
className="w-full"
src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4"
autoPlay
muted
playsInline
loop
/>
<SeekControls />
</Player.Container>
</Player.Provider>
);
}
@@ -10,6 +10,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
import BasicUsageDemoReact from "@/components/docs/demos/create-player/react/css/BasicUsage";
import basicUsageReactTsx from "@/components/docs/demos/create-player/react/css/BasicUsage.tsx?raw";
import basicUsageReactCss from "@/components/docs/demos/create-player/react/css/BasicUsage.css?raw";
import BasicUsageDemoReactTailwind from "@/components/docs/demos/create-player/react/tailwind/BasicUsage";
import basicUsageReactTailwindTsx from "@/components/docs/demos/create-player/react/tailwind/BasicUsage.tsx?raw";
`createPlayer` is the entry point for setting up a Video.js player in React. It accepts a configuration object with a `features` array and returns hooks and components for building a player.
@@ -38,6 +40,15 @@ const Player = createPlayer({ features: videoFeatures });
<BasicUsageDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
]}
>
<BasicUsageDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="createPlayer" />
@@ -11,6 +11,9 @@ import BasicUsageDemoHtml from "@/components/docs/demos/html-create-player/html/
import basicUsageHtml from "@/components/docs/demos/html-create-player/html/css/BasicUsage.html?raw";
import basicUsageHtmlCss from "@/components/docs/demos/html-create-player/html/css/BasicUsage.css?raw";
import basicUsageHtmlTs from "@/components/docs/demos/html-create-player/html/css/BasicUsage.ts?raw";
import BasicUsageDemoHtmlTailwind from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.astro";
import basicUsageTailwindHtml from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.html?raw";
import basicUsageTailwindHtmlTs from "@/components/docs/demos/html-create-player/html/tailwind/BasicUsage.ts?raw";
`createPlayer` is the entry point for setting up a Video.js player with HTML custom elements. It accepts a configuration object with a `features` array and returns a typed <DocsLink slug="reference/player-controller">`PlayerController`</DocsLink>, `context`, `ProviderMixin`, `ContainerMixin`, and a `create` store factory.
@@ -71,4 +74,13 @@ class ComposedPlayer extends ProviderMixin(ContainerMixin(MediaElement)) {}
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo files={[
{ title: "index.html", code: basicUsageTailwindHtml, lang: "html" },
{ title: "index.ts", code: basicUsageTailwindHtmlTs, lang: "ts" },
]}>
<BasicUsageDemoHtmlTailwind />
</Demo>
</StyleCase>
<UtilReference util="createPlayer" slug="html-create-player" />
@@ -12,6 +12,9 @@ import BasicUsageDemoHtml from "@/components/docs/demos/player-controller/html/c
import basicUsageHtml from "@/components/docs/demos/player-controller/html/css/BasicUsage.html?raw";
import basicUsageHtmlCss from "@/components/docs/demos/player-controller/html/css/BasicUsage.css?raw";
import basicUsageHtmlTs from "@/components/docs/demos/player-controller/html/css/BasicUsage.ts?raw";
import BasicUsageDemoHtmlTailwind from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.astro";
import basicUsageTailwindHtml from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.html?raw";
import basicUsageTailwindHtmlTs from "@/components/docs/demos/player-controller/html/tailwind/BasicUsage.ts?raw";
`PlayerController` is a reactive controller that consumes the player store from <DocsLink slug="reference/player-context">`playerContext`</DocsLink>. Without a selector it returns the store instance directly (no subscription — use this for actions). With a selector it returns the selected value and subscribes to changes, triggering a host update on shallow-equal change. Access the current value via `.value`, which returns `undefined` until connected to a provider.
@@ -31,6 +34,16 @@ import basicUsageHtmlTs from "@/components/docs/demos/player-controller/html/css
<BasicUsageDemoHtml />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "index.html", code: basicUsageTailwindHtml, lang: "html" },
{ title: "index.ts", code: basicUsageTailwindHtmlTs, lang: "ts" },
]}
>
<BasicUsageDemoHtmlTailwind />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="PlayerController" />
@@ -11,6 +11,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
import BasicUsageDemoReact from "@/components/docs/demos/render-element/react/css/BasicUsage";
import basicUsageReactTsx from "@/components/docs/demos/render-element/react/css/BasicUsage.tsx?raw";
import basicUsageReactCss from "@/components/docs/demos/render-element/react/css/BasicUsage.css?raw";
import BasicUsageDemoReactTailwind from "@/components/docs/demos/render-element/react/tailwind/BasicUsage";
import basicUsageReactTailwindTsx from "@/components/docs/demos/render-element/react/tailwind/BasicUsage.tsx?raw";
`renderElement` renders a UI component element, handling default tag rendering, render props (element or function), props merging, ref composition, and state-driven `className`/`style`.
@@ -66,6 +68,15 @@ The `render` prop lets consumers fully customize the rendered element while pres
<BasicUsageDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
]}
>
<BasicUsageDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="renderElement" />
@@ -11,6 +11,8 @@ import StyleCase from "@/components/docs/StyleCase.astro";
import BasicUsageDemoReact from "@/components/docs/demos/use-button/react/css/BasicUsage";
import basicUsageReactTsx from "@/components/docs/demos/use-button/react/css/BasicUsage.tsx?raw";
import basicUsageReactCss from "@/components/docs/demos/use-button/react/css/BasicUsage.css?raw";
import BasicUsageDemoReactTailwind from "@/components/docs/demos/use-button/react/tailwind/BasicUsage";
import basicUsageReactTailwindTsx from "@/components/docs/demos/use-button/react/tailwind/BasicUsage.tsx?raw";
`useButton` provides button behavior including keyboard activation and accessibility checks. It returns a `getButtonProps` function for spreading onto a `<button>` element and a `buttonRef` for validation.
@@ -31,6 +33,15 @@ import basicUsageReactCss from "@/components/docs/demos/use-button/react/css/Bas
<BasicUsageDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: basicUsageReactTailwindTsx, lang: "tsx" },
]}
>
<BasicUsageDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="useButton" />
@@ -11,9 +11,13 @@ import StyleCase from "@/components/docs/StyleCase.astro";
import StoreAccessDemoReact from "@/components/docs/demos/use-player/react/css/StoreAccess";
import storeAccessReactTsx from "@/components/docs/demos/use-player/react/css/StoreAccess.tsx?raw";
import storeAccessReactCss from "@/components/docs/demos/use-player/react/css/StoreAccess.css?raw";
import StoreAccessDemoReactTailwind from "@/components/docs/demos/use-player/react/tailwind/StoreAccess";
import storeAccessReactTailwindTsx from "@/components/docs/demos/use-player/react/tailwind/StoreAccess.tsx?raw";
import SelectorDemoReact from "@/components/docs/demos/use-player/react/css/Selector";
import selectorReactTsx from "@/components/docs/demos/use-player/react/css/Selector.tsx?raw";
import selectorReactCss from "@/components/docs/demos/use-player/react/css/Selector.css?raw";
import SelectorDemoReactTailwind from "@/components/docs/demos/use-player/react/tailwind/Selector";
import selectorReactTailwindTsx from "@/components/docs/demos/use-player/react/tailwind/Selector.tsx?raw";
`Player.usePlayer` gives you access to the player store from any component within a `Player.Provider`. It has two overloads — without arguments for direct store access, or with a selector for reactive state subscriptions. `Player.usePlayer` is typed to the features you passed to <DocsLink slug="reference/create-player">`createPlayer`</DocsLink>. It's also available as a standalone import (`import { usePlayer } from '@videojs/react'`), but the standalone version returns an untyped store.
@@ -34,6 +38,15 @@ Call `Player.usePlayer()` without arguments to get the store instance. Use this
<StoreAccessDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: storeAccessReactTailwindTsx, lang: "tsx" },
]}
>
<StoreAccessDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
### Selector Subscription
@@ -51,6 +64,15 @@ Pass a selector function to subscribe to specific state. The component re-render
<SelectorDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: selectorReactTailwindTsx, lang: "tsx" },
]}
>
<SelectorDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="usePlayer" />
@@ -11,9 +11,13 @@ import StyleCase from "@/components/docs/StyleCase.astro";
import StoreAccessDemoReact from "@/components/docs/demos/use-store/react/css/StoreAccess";
import storeAccessReactTsx from "@/components/docs/demos/use-store/react/css/StoreAccess.tsx?raw";
import storeAccessReactCss from "@/components/docs/demos/use-store/react/css/StoreAccess.css?raw";
import StoreAccessDemoReactTailwind from "@/components/docs/demos/use-store/react/tailwind/StoreAccess";
import storeAccessReactTailwindTsx from "@/components/docs/demos/use-store/react/tailwind/StoreAccess.tsx?raw";
import SelectorDemoReact from "@/components/docs/demos/use-store/react/css/Selector";
import selectorReactTsx from "@/components/docs/demos/use-store/react/css/Selector.tsx?raw";
import selectorReactCss from "@/components/docs/demos/use-store/react/css/Selector.css?raw";
import SelectorDemoReactTailwind from "@/components/docs/demos/use-store/react/tailwind/Selector";
import selectorReactTailwindTsx from "@/components/docs/demos/use-store/react/tailwind/Selector.tsx?raw";
`useStore` subscribes to a store instance directly. It has the same two overloads as <DocsLink slug="reference/use-player">`usePlayer`</DocsLink> — without a selector for store access, or with a selector for reactive state. Within a player provider, `usePlayer` is usually simpler since it reads the store from context. Reach for `useStore` when you have a store instance directly or need to derive computed values from the store.
@@ -34,6 +38,15 @@ Call `useStore(store)` without a selector to get the store instance back for imp
<StoreAccessDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: storeAccessReactTailwindTsx, lang: "tsx" },
]}
>
<StoreAccessDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
### Selector Subscription
@@ -51,6 +64,15 @@ Pass a selector to derive and subscribe to computed values from the store. The c
<SelectorDemoReact client:idle />
</Demo>
</StyleCase>
<StyleCase styles={["tailwind"]}>
<Demo
files={[
{ title: "App.tsx", code: selectorReactTailwindTsx, lang: "tsx" },
]}
>
<SelectorDemoReactTailwind client:idle />
</Demo>
</StyleCase>
</FrameworkCase>
<UtilReference util="useStore" />