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

Html Widget Laravel Package

victoire/html-widget

victoire/html-widget is a Laravel/PHP package for defining, managing, and rendering reusable HTML widgets. It helps embed custom markup cleanly while keeping widget content organized, reusable, and easy to integrate into dashboards, CMS blocks, and dynamic page sections.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before anything else. Despite the "Laravel package" framing in the description, victoire/html-widget is a Symfony/Victoire CMS widget bundle from 2016 — Doctrine entity, Symfony form type, Twig templates, AppKernel registration. composer require victoire/html-widget in a Laravel app gives you no service provider, no facade, no config file, no Blade directive. Spend ten minutes in the repo confirming this (*Bundle.php, Entity/, Form/, Resources/views/) before planning around it.

If you're on legacy Victoire/Symfony: register VictoireWidgetHtmlBundle in AppKernel.php, run doctrine:schema:update --force (missing this is the #1 silent failure), open a page in the editor, drop the HTML widget into a slot, paste an embed, clear cache.

If you're on Laravel (the likely case): don't install it — copy the pattern. You need roughly:

  • a widgets table (key, title, html, is_active, updated_by, timestamps),
  • an HtmlWidget model plus a WidgetRenderer contract bound in the container,
  • a Blade component <x-html-widget key="sidebar-promo" /> that echoes {!! $html !!} inside its own wrapper,
  • a policy gate for the "trusted raw HTML" permission.

Where to look first in the repo — these three files are the whole spec, and are ~30 lines to translate:

  1. Entity/WidgetHtml.php — the persisted field shape.
  2. Form/WidgetHtmlType.php — what the editor UI actually exposes.
  3. Resources/views/ — the front-end markup contract.

First use case: a dashboard or sidebar "notice/embed" block — marketing pastes an iframe, form snippet, or JSON-LD into an admin textarea; the component renders it verbatim. That's the smallest valuable slice and matches the original package's intent.

Implementation Patterns

Funnel every render through one entry point. Never {!! $widget->html !!} inline in page templates. One HtmlWidget::render($key) (or a <x-html-widget> component) is the only place raw output happens — sanitization, caching, kill-switch, and logging all live there.

Key-based storage beats page-based storage. A key column lets the same snippet appear on multiple pages, gives you an audit trail (updated_by), and makes cache-busting trivial. This mirrors how the Victoire bundle attaches one widget instance to a page slot.

Template owns the container, widget owns the inner embed. Your component emits <div class="widget widget--{key}">…</div>; the CMS/admin only stores the fragment. Otherwise editors paste <div> wrappers inconsistently and layout changes require content edits.

Sanitize on write, not on read. Use mews/purifier or symfony/html-sanitizer with an allowlist in a model mutator / saving observer. Or split into html_sanitized vs html_trusted with a boolean gate tied to a permission. Sanitizing on read means N sanitizer calls per request and a moving target for your allowlist.

Cache and bust via observer. Cache::rememberForever("widget:{$key}") in the renderer, with a saved/deleted observer calling Cache::forget. Widget HTML is static; there's no reason to hit the DB per page request.

Seed, don't click. For migrations from Victoire (or any legacy CMS), export rows and map fields into a seeder or a one-off artisan command. Scripting the editor UI is fragile and slow.

Feature-flag the whole thing. config('widgets.enabled') checked in the renderer gives you an incident response — turn off raw HTML rendering without a deploy if something malicious gets through.

Site-wide snippets belong in the layout. Analytics, chat widgets, cookie banners — these go in the base Blade layout, not duplicated per page via the widget mechanism. Same advice the Victoire bundle implicitly requires.

Gotchas and Tips

  • The package is not Laravel. No composer.json extra.laravel block, no service provider, no publishable config. Don't burn a sprint trying to make Symfony bundle bootstrapping work in Laravel's container — the adapter costs more than a native implementation.
  • Abandoned since Aug 2016, 2 stars, 0 dependents. No security fixes, no PHP 8 / Symfony 4+ compatibility, no community to lean on. If you truly adopted the Victoire bundle, vendor the code and own it. If you're on Laravel, you're reimplementing — which is the correct answer anyway.
  • XSS is the entire threat model. A raw HTML field renders unescaped by design. Enforce: trusted-role-only editing, sanitizer allowlist on write, updated_by audit column, and log the diff. A compromised low-privilege editor is a stored-XSS vector.
  • Editor footguns are the second bug class. Unclosed tags break the surrounding page layout; pastes from Google Docs carry inline styles; iframes need title/sandbox attributes. Add a save-time validation step (sanitizer + tag balance) and consider rendering a preview inside a sandboxed iframe.
  • Cache invalidation is the most likely production bug in a reimplementation — stale widgets after an edit. Use a model observer, and namespace cache keys by key plus a content version if you ever A/B.
  • Schema drift bites both stacks. In the Victoire bundle it's the missed doctrine:schema:update; in Laravel it's the migration you forgot after adding a column. Same class of bug, easier to reproduce locally.
  • Extension points to copy: on Victoire, override via app/Resources/VictoireWidgetHtmlBundle/views/ rather than forking the entity — keeps you upgradeable if upstream ever moves. In Laravel, the equivalent is a WidgetRenderer interface bound in AppServiceProvider, so you can swap to a sanitized/sandboxed renderer without touching call sites.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core