Skip to main content
Astro renders the Bunny Player iframe on the server and ships no JavaScript until you ask for some. This guide asks for very little: a <bunny-player> custom element that attaches player.js and turns the player’s events into DOM events.

Astro example on GitHub

An Astro site with custom controls and an event log.

Quickstart

1

Install player.js

player.js ships without types. Add a declaration file anywhere your tsconfig.json includes, for example player.js.d.ts. It covers the methods and events the Bunny Player supports:
player.js.d.ts
2

Create the component

The embed URL waits in data-src until the element has a Player listening. An iframe with src in the HTML can finish loading first, and then ready never arrives.
src/components/BunnyPlayer.astro
3

Add your video IDs

Copy the library ID and video GUID from the video’s page in the dashboard. Both are public, since they appear in every embed URL.
.env
Astro inlines PUBLIC_ variables at build time. Without them, the embed URL reads undefined/undefined.
4

Add it to a page

src/pages/index.astro
params takes any player parameter.

Control playback

ready hands over the Player as event.detail.
Getters answer through a callback, since the value comes back from the iframe. Playback speed is missing from the npm build (0.1.0), and send() covers the gap:
Browsers block unmuted play() before the viewer has clicked anything. Mute first if playback has to start on its own. The Playback control API lists every method and event.

Track progress

timeupdate fires several times a second. Throttle it before it reaches your backend.
Pass the saved position back as params={{ t: savedSeconds }} to resume.

Signed embed URLs

With embed view token authentication on, sign the URL in the page frontmatter and pass token and expires through params. Render that page on demand. A prerendered page hands every visitor the same token, and it expires. The signing code is in Sign embed URLs on the server.

Load player.js from the CDN instead

We host a build of player.js that adds setPlaybackRate() and the playbackratechange event (Methods). Load it from the component, so only pages with a player fetch it.
src/lib/load-playerjs.ts
Swap the player.js import in BunnyPlayer.astro for the loader, and wait for it before setting src.
src/components/BunnyPlayer.astro

Troubleshooting

The iframe loaded before player.js was listening. Keep the URL in data-src and let the element set src.A hidden tab also holds ready back until the viewer switches to it.
<ClientRouter /> runs page scripts once, leaving your ready listener on the previous page’s element. Register it inside an astro:page-load listener.
The library’s allowed domains, direct access block, or token authentication is rejecting the embed. See Embedding restrictions.
Last modified on October 7, 2026