Statamic's video field grew up
Since October, Statamic’s video field recognizes the provider and video ID of a link, and it supports Cloudflare Stream. The release notes cover this in a few short lines, but behind them is one large contribution, split into five pieces, three of which shipped.
· 9 min read · Thomas Sausen
Between the monthly posts
Statamic's October roundup covers 6.36 to 6.38 and the changes you can see in the control panel: dragging assets into folders, Live Preview before the first save, reordering sets with the keyboard. A monthly post can't show a change that started long before the month and only partly shipped.
The video fieldtype is that change. Between 6.32 on September 9 and 6.38 on October 23, it went from a plain-text URL input to a field that recognizes the provider and the video ID. It can also build the embed URL, with or without tracking. Cloudflare Stream joined YouTube and Vimeo. The release notes show six short entries across four releases. Behind them is a single pull request that grew too large to merge in one go.
Where it started
For most of its life, the video fieldtype stored a string and returned that string. Everything clever happened in modifiers. The embed_url modifier turned a YouTube or Vimeo page link into a player URL, and trackable_embed_url did the same without the privacy tweaks. A third one, is_embeddable, told you whether to reach for an iframe or a <video> tag. Statamic's own template scaffolding still renders the field that way:
{{ if video | is_embeddable }}
<iframe src="{{ video | embed_url }}"></iframe>
{{ else }}
<video src="{{ video | embed_url }}"></video>
{{ /if }}That works, and it keeps working. But the field itself knew nothing about its content. If you wanted the YouTube ID for a thumbnail or a click-to-load facade, you parsed the URL yourself.
One pull request, five pieces
It began with statamic/ideas#1336, a request to support Cloudflare Stream in the video fieldtype. Erin Dalzell picked it up in #11871, which grew well beyond that: a provider dropdown, a cloudflare:<id> storage format, a lookup endpoint, and oEmbed support for a long list of providers through the Embera library. By the end, it touched 17 files and roughly 1,000 lines.
A change that size raises more questions, and Jason Varga's review worked through them. Some were about the control panel: how provider markup gets there, and what happens when lookups overlap while someone types. Others were about how Embera handles network requests and what templates should receive for each provider.
Erin addressed them and then split the pull request into five, so each part could be reviewed and shipped on its own:
#15456: debounce field updates. Merged, shipped in 6.33.
#15457: render direct video files in a
<video>element. Closed.#15458: the
Embedvalue object. Merged, shipped in 6.35.#15459: Cloudflare Stream. Merged, shipped in 6.38.
#15460: oEmbed lookups through Embera. Closed.
The split had a practical rationale. Cloudflare Stream needed no new dependency; only the oEmbed piece did. So whether every Statamic install should pull in Embera no longer blocked everything else.
Four releases, six weeks
Release | Date | What changed |
|---|---|---|
6.32.0 | Sep 9 | Assets treat .mpeg, .mpg and .mkv as video (#15378) |
6.33.0 | Sep 16 | The field input is debounced (#15456) |
6.35.0 | Oct 1 | Augmentation to an |
6.38.0 | Oct 23 | Cloudflare Stream support (#15459) |
The 6.35 entries need a word. Jason followed #15458 with #15554 about twelve hours later, before the release went out. The object had been built from five constructor arguments with public readonly properties ($video->provider). Now it's built from the value alone and exposes methods ($video->provider()). Only the provider is worked out up front, everything else on demand, which leaves room for providers that are expensive to resolve. The same pull request fixed trackable_embed_url, which still ran its own YouTube and Vimeo rewriting on the raw URL and would have produced broken output for any other provider.
The 6.32 change is a separate contribution and easy to overlook. Since 6.35, though, the field decides whether a URL points to a video file by checking it against that same extension list. The debounce in 6.33 means the field no longer pushes an update on every keystroke while you type or paste a URL.
One loose end: the control panel preview still only recognizes .mp4, .ogv, .mov and .webm. A .mkv or .m4v link is treated as a video file in your templates but gets no preview in the entry.
What your templates get now
Since 6.35, a video field augments to Statamic\Fieldtypes\Video\Embed. It extends ArrayableString and casts back to the URL the editor entered, so {{ video }}, {{ video ?? 'fallback' }}, and existing modifier chains behave as before.
Keeping that promise took a few rounds. The review compared the branch with 6.x and found cases where the object and the plain string behaved differently, for example in empty fields or with trackable URLs. Instead of fixing these cases one by one, the pull request added a ParityTest. It checks the augmented object against the raw string for every modifier and every kind of value. Cleared fields in localizations store an empty string, and Antlers fallbacks like ?? and or keep working there because Embed extends ArrayableString.
In Antlers, the object exposes five keys:
Key | Value |
|---|---|
|
|
| The provider's video ID |
| The hash of an unlisted Vimeo video |
| The privacy-friendly player URL |
| What the editor entered |
In Blade or PHP you call methods: provider(), id(), privacyHash(), embedUrl(), url(), plus trackableEmbedUrl(), isEmbeddable() and isSupported(). Those last three aren't in the Antlers array. There you keep using trackable_embed_url and is_embeddable, which now hand off to the object.
The ID is the most useful addition for templates. A click-to-load YouTube facade, for example, no longer needs a helper of its own:
{{ if video:provider == 'youtube' }}
<lite-youtube videoid="{{ video:id }}"></lite-youtube>
{{ /if }}The ID parser is stricter than you might expect. The tests cover mobile links, youtu.be with timestamps, embed and nocookie URLs, links without a scheme, and they return null for malformed IDs such as watch?v=x"><script>. That's the kind of input you'd otherwise have to guard against in your own code.
The privacy split hasn't changed, but it now lives in one place. embed_url uses youtube-nocookie.com and adds dnt=1 for Vimeo. The trackable version leaves both out. One inconsistency is pinned in the tests: for a youtube.com/watch link with &t=2, embed_url converts the timestamp to start=2, while the trackable URL keeps t=2. If you rely on start times with trackable embeds, check the output.
Cloudflare Stream
Since 6.38, the field accepts Cloudflare Stream URLs on your customer subdomain, customer-<code>.cloudflarestream.com. A bare video URL, an /iframe link, and a /watch link all resolve to the iframe player. Player options in the query string, such as autoplay, muted, or poster, are kept and re-encoded. On a watch link they're dropped, since they only apply to the iframe.
The first version looked different: a dropdown to choose between "URL" and "Cloudflare Stream", and a separate ID input that stored cloudflare:<id>. Jason asked whether Cloudflare offered a URL to paste instead, since "URL" already covered YouTube, Vimeo, and video files. Erin, who uses Stream, came back with the URL formats, and the dropdown went. The final version detects the watch link behind the big blue button on a video page in the Cloudflare dashboard.
Manifest URLs were left out on purpose. They overlap with plain video file URLs, and treating them as Cloudflare URLs could have changed how existing sites render them.
The edge cases are handled deliberately:
A download link (
/downloads/default.mp4) counts as afile.HLS and DASH manifest URLs are rejected, and the control panel tells editors to use the URL from the Video Link button in Cloudflare instead.
Thumbnail URLs and links without a customer subdomain are
unsupported.trackableEmbedUrl()returns the same player URL, because Cloudflare has no tracking-free variant to switch between.
The test cases cover whatever editors are likely to paste from Cloudflare's dashboard, plus a few hostile inputs: HTML in the query string, a path traversal in the ID, a trailing newline. All of them end up either encoded or unsupported.
What didn't ship
Two of the five pieces didn't make it in: rendering direct video files in a <video> element (#15457) and oEmbed lookups for providers like Loom, Wistia, and TikTok (#15460). The oEmbed version was worked out in detail, but it would have added a dependency to every install for something nobody had specifically asked for, and Erin closed it.
Because the Embed class computes everything except the provider on demand, a provider that needs a lookup can still be added later, in core or as an addon.
Before you update
If your templates only output the field or run it through modifiers, there's nothing to do. Two things are worth a quick search through your own code:
PHP that checks the augmented value with
is_string()or compares it strictly with===. It's an object now. Cast it with(string)or callurl().Self-hosted .mkv or .m4v links. They work in your templates as
file, but editors won't see a preview in the control panel.
The REST API is unaffected and still returns the plain URL string. An early version of #15458 serialized the whole object, which the review caught before it shipped.
Sources
- The Little Things Add Up (opens in new tab), Statamic, October 27, 2026
- Release Notes (opens in new tab), Statamic
- Add Cloudflare Stream to Video fieldtype (opens in new tab), statamic/ideas on GitHub
- Support more video providers, including Cloudflare Stream (opens in new tab), statamic/cms on GitHub
- Add video asset extensions (opens in new tab), statamic/cms on GitHub, September 7, 2026
- Debounce video fieldtype updates (opens in new tab), statamic/cms on GitHub, September 15, 2026
- Render direct video files in a video element (opens in new tab), statamic/cms on GitHub
- Augment video fields to a value object carrying the provider (opens in new tab), statamic/cms on GitHub, September 30, 2026 •
- Add id and privacy hash to augmented video values (opens in new tab), statamic/cms on GitHub, September 30, 2026
- Make the video Embed method based and add trackable embed urls, (opens in new tab), statamic/cms on GitHub, October 1, 2026
- Look up video providers via oEmbed (opens in new tab), statamic/cms on GitHub
- Add Cloudflare Stream support to the video fieldtype (opens in new tab), statamic/cms on GitHub, October 23, 2026
- Video Embed source and tests (opens in new tab), statamic/cms on GitHub
Thomas Sausen is a freelance web developer and the editor of Flatbard. He builds with Statamic and writes about the surrounding ecosystem.