Start here
Providers
One markup pattern and one API across four backends. Fideo works out which provider a URL belongs to, loads only the SDK that provider needs, and normalizes the embed URL so the same options behave the same way everywhere.
| Provider | Element | URLs it claims |
|---|---|---|
| html5 | <video> |
Any source the browser can play. The default for every <video> element. |
| youtube | <iframe> |
youtube.com, youtube-nocookie.com, youtu.be |
| vimeo | <iframe> |
vimeo.com, including player.vimeo.com |
| wistia | <iframe> |
wistia.com, wistia.net, fast.wistia.* |
How detection works
A <video> is always html5. For an <iframe>, Fideo tests
candidate URLs in order — the element's src, then sources.desktop,
sources.tablet, sources.mobile — and takes the first provider that matches. If
nothing matches it falls back to html5, which will throw for an iframe, so an unrecognised
embed URL needs an explicit provider.
<iframe data-fideo data-fideo-provider="vimeo" src="https://my-proxy.example.com/embed/123" ></iframe>
provider is one of the few options where JavaScript beats the data attribute.
Everything scalar works the other way around — see
precedence.
HTML5 video
The richest provider: it is the only one that reports a real buffered range, and the only one that can use a
native poster attribute alongside Fideo's own poster overlay.
<video data-fideo src="/media/clip.mp4" poster="/media/poster.jpg"></video>
YouTube
Fideo accepts watch URLs, short links, Shorts and existing embeds, and rewrites all of them to a
youtube-nocookie.com/embed/… URL with the JS API enabled and YouTube's own chrome switched off.
| You write | Fideo loads |
|---|---|
| youtube.com/watch?v=ID | youtube-nocookie.com/embed/ID |
| youtu.be/ID | |
| youtube.com/shorts/ID | |
| youtube.com/embed/ID |
- Query parameters survive normalization, so
?start=30still applies. - Looping adds
playlist=IDautomatically, which YouTube requires for a single video to repeat. - No-cookie is not no-tracking. It reduces cookies on load; playback can still send data to YouTube, so consent rules may still apply.
Vimeo
Public, private and unlisted videos all work. A private URL carries its hash as a path segment, which Fideo
moves into the h= parameter the embed player expects.
https://vimeo.com/123456789/5e2d1c1e6d
→ https://player.vimeo.com/video/123456789?h=5e2d1c1e6d
// An existing embed URL with ?h= is left alone
Wistia
Wistia is the one provider that does not stay an iframe. Fideo reads the media ID out of the URL, replaces
the <iframe> with a <wistia-player> custom element, and drives that
instead.
player.element still points at the
original iframe reference, but the visible node is a <wistia-player>. Style it through the
.fideo__media class, which Fideo copies across.
Lazy loading
Every iframe provider is lazy by default. Fideo strips the src into
data-fideo-lazy-src, watches the element with an IntersectionObserver, and only
then loads the provider SDK and the embed. Nothing is requested for a player the visitor never scrolls to.
| Option | Default | Notes |
|---|---|---|
| lazy | true for iframes, false for <video> | Set data-fideo-lazy="false" to load an embed immediately. |
| lazyRootMargin | '800px 0px' | How far ahead of the viewport loading begins. |
- Calling
play()forces the upgrade — a lazy player that is asked to do something loads immediately rather than waiting to be scrolled into view. - Volume and muted state set before loading are buffered and applied to the real adapter once it exists.
- Without
IntersectionObserverthe player loads eagerly instead of never.
Skipping providers you do not use
disabledProviders guarantees a provider's SDK is never fetched. Asking for a disabled provider
throws, and initFideo() logs a warning and moves on to the next element rather than failing the
whole page.
initFideo({ disabledProviders: ['youtube', 'vimeo', 'wistia'], });
What each provider loads
| Provider | Fetched at mount |
|---|---|
| html5 | Nothing — the browser's own media element |
| youtube | youtube.com/iframe_api |
| vimeo | player.vimeo.com/api/player.js |
| wistia | fast.wistia.com/player.js + /embed/<id>.js |
Each script is requested once per page no matter how many players use it, and only when a player of that provider actually loads.
Capability differences
The API is identical across providers, but the data underneath is not.
| Behaviour | html5 | youtube | vimeo | wistia |
|---|---|---|---|---|
| Play, pause, seek, volume, rate | Yes | Yes | Yes | Yes |
state.buffered | Real value | Always 0 | Always 0 | Always 0 |
| Time updates | Native events | Polled every 250 ms | Native events | Native events |
| Lazy by default | No | Yes | Yes | Yes |
| Background cover sizing | object-fit | Calculated | Calculated | fit-strategy |