Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Video Widget Laravel Package

friendsofvictoire/video-widget

A small Laravel package that provides a reusable video widget for embedding and playing videos in your app. Drop it into your views to render video content without writing custom markup, keeping playback setup and presentation in one place.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you type composer require, confirm what this package actually is: friendsofvictoire/video-widget is a Symfony bundle (FriendsOfVictoire\WidgetVideoBundle) built for the Victoire CMS. It has no Laravel service provider, no auto-discovery entry, no publishable config, and no Blade views. Installing it in a Laravel app will fail on platform constraints long before it does anything useful. Treat it as a reference implementation you read for the pattern, not a dependency you adopt.

Read it locally without booting Symfony:

git clone https://github.com/FriendsOfVictoire/WidgetVideoBundle /tmp/video-widget

Look at these first, in this order:

  1. Widget/VideoWidget.php — the widget class. Closest thing to a "model": declares the entity name, the form type, and the Twig template. This is the contract you're replicating.
  2. Form/VideoType.php — the Symfony form. This is your schema: what an editor can configure (URL/ID, provider, title, dimensions).
  3. Resources/views/ — the Twig template producing the <iframe>/embed markup. This is the piece you will actually port.
  4. DependencyInjection/ — how the bundle wires itself and what it assumes from Victoire.

First use case to target: a single configurable video embed block — an editor picks a provider (YouTube/Vimeo/Dailymotion) and pastes a URL or ID, and the page renders a responsive, privacy-respecting embed. That is ~80% of what the bundle does, and roughly 60 lines of Laravel to reproduce. Timebox a one-day spike; you will not beat that by adopting.

Your first real decision is not "how do I install this," it is "what does the bundle's schema tell me I need" — provider allowlist, ID extraction, title/caption, aspect ratio, consent gating. Extract those into a requirements checklist, then build the Laravel-native equivalent against it.

Implementation Patterns

Port the concept, not the code. The bundle's value is the shape: a small config object + a form + a render template. In Laravel that maps cleanly to a Blade component plus a config JSON column on a content block, or a Filament/Livewire field group if editors configure in an admin panel.

Start with a config-driven provider registry, so adding a provider is a data change rather than a code change:

// config/video.php
return [
    'providers' => [
        'youtube' => [
            'pattern' => '~youtu\.?be(?:\.com)?/(?:watch\?v=|embed/|shorts/)?([A-Za-z0-9_-]{11})~',
            'embed'   => 'https://www.youtube-nocookie.com/embed/%s',
        ],
        'vimeo' => [
            'pattern' => '~vimeo\.com/(?:video/)?(\d+)~',
            'embed'   => 'https://player.vimeo.com/video/%s',
        ],
    ],
    'allow_consent_gating' => env('VIDEO_CONSENT_GATING', true),
];

Then a framework-agnostic resolver that normalizes arbitrary pasted URLs into a provider + id + embed src:

// app/Support/VideoEmbed.php
final class VideoEmbed
{
    public static function fromUrl(string $url, ?string $provider = null): ?array
    {
        foreach (config('video.providers') as $key => $cfg) {
            if ($provider && $provider !== $key) {
                continue;
            }
            if (preg_match($cfg['pattern'], $url, $m)) {
                return ['provider' => $key, 'id' => $m[1], 'src' => sprintf($cfg['embed'], $m[1])];
            }
        }
        return null;
    }
}

Blade component as the widget renderer. The Twig template is essentially a hardcoded iframe. Keep the markup in one place and make it responsive, accessible, and lazy by default:

{{-- resources/views/components/video-embed.blade.php --}}
@props(['src', 'title' => null])
<div {{ $attributes->class('relative aspect-video') }}>
    <iframe
        src="{{ $src }}"
        title="{{ $title ?? 'Embedded video' }}"
        class="absolute inset-0 h-full w-full"
        loading="lazy"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen
        referrerpolicy="strict-origin-when-cross-origin"
    ></iframe>
</div>

Usage from a content block:

@php($video = \App\Support\VideoEmbed::fromUrl($block->config['url'] ?? ''))
@if($video)
    <x-video-embed :src="$video['src']" :title="$block->config['title'] ?? null" />
@endif

Storage pattern. Store the editor's raw input (url, title, optional provider override) as JSON on the block, not the derived src. Resolve at render time. That way a provider URL-format change or a domain switch (e.g. moving to youtube-nocookie.com) is a config/deploy change, not a data migration. If you must persist the resolved src for performance, cache it keyed by the raw URL and invalidate on config change.

Consent gating as an application concern. The bundle will inject an iframe unconditionally; you should not. Wrap the embed in a consent component that renders a poster/thumbnail with a "load video" button, and only mounts the iframe after consent. This is the part teams most often forget and then have to retrofit across every page.

Filament/Livewire surface. If editors configure in an admin, a simple TextInput for URL plus a Select for optional provider override is enough. If you use Filament, its native field types plus a custom view cover this without any third-party bundle — you do not need the Victoire form type at all.

Testing. The highest-value tests are provider-detection and render output. Table-drive the fromUrl cases (watch/shorts/embed/youtu.be/vimeo/query params/garbage), and assert the Blade component emits loading="lazy", a title, and the expected src host. This catches the silent-breakage class of bug before it reaches production.

Gotchas and Tips

Do not composer require this in Laravel. Expect a hard conflict on PHP and Symfony versions (the bundle's vintage caps near PHP 7.x / Symfony ^2.7|^3.x, which collides with Laravel 10/11's PHP 8.1+ and Symfony 6/7). Never force it with --ignore-platform-reqs — that defers the failure to runtime, in the least debuggable place. If the target really is a legacy Symfony/Victoire estate, register via bundles.php, configure the widget templates, and immediately budget for a fork.

Bus factor one, zero dependents. One star and zero dependents means no upstream maintenance, no security response, no community validation. If you adopt the code in any form, you own it — pin it, vendor it, and assign an owner before it touches production. Do not let it appear in composer.json as a "supported dependency."

Provider URL drift is the #1 silent failure. Providers change URL formats and add new paths (Shorts, /live/, localized domains). Your regexes will quietly stop matching, the embed disappears, and nobody notices until a customer complains. Keep the patterns in config, add a monitoring check that resolves a known sample URL per provider on deploy, and log misses rather than swallowing them.

oEmbed calls belong on the server and must be cached. If you resolve titles/thumbnails via oEmbed (or oscarotero/embed), cache aggressively — third-party rate limits and latency will otherwise show up directly in page render time. Never call oEmbed on the render path without a cache.

Consent and CSP are your job. GDPR cookie gating, frame-src in your CSP, and youtube-nocookie.com selection are all application concerns the bundle does not solve. Decide the consent model up front; retrofitting a "click to load" facade across an existing site is far more expensive than designing it in.

Accessibility is easy to get wrong. Every iframe needs a meaningful title attribute — an empty or generic one fails audits. Prefer aspect-video CSS over fixed width/height so embeds stay responsive, and carry over allowfullscreen.

Thumbnails 404 quietly. If you build a consent facade on a thumbnail, some videos have no max-res thumbnail or the provider changes the URL scheme. Have a fallback placeholder and a test that fetches one thumbnail per provider.

Extension points are all yours to define. There is no plugin system to build on — the "extension point" is the config array and the Blade component. That is actually fine: it keeps the surface small, testable, and easy to hand to another developer, which is precisely the advantage a 60-line first-party implementation has over an abandoned single-maintainer bundle.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib