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

Widget Laravel Package

orchestra/widget

Orchestra Widget provides a lightweight widget manager for Laravel, letting you register, resolve, and render reusable UI components within the Orchestra Platform ecosystem. It integrates with the framework's container and view layer to keep dashboards and admin panels modular.

View on GitHub
Deep Wiki
Context7

Getting Started

Day one with orchestra/widget is about confirming you're in the right context, not writing widgets. The package is a first-party component of Orchestra Platform, so the realistic starting point is an app that already boots Orchestra's extension manager, memory/config system, and ACL. If you're on stock Laravel, treat this as a reference implementation and read src/ rather than composer require-ing it — the wiring cost exceeds the value of the registry.

Confirm three things before you write a line of widget code:

  1. Composer resolves. v6.0.0 targets Laravel 8; check php and illuminate/* constraints against your runtime. A constraint deadlock against other Orchestra packages is the most likely early blocker and the cheapest to find.
  2. It boots standalone-ish under Orchestra Testbench. Run the package suite in an isolated branch. Capture the result as a go/no-go artifact — you'll want it when someone asks "why didn't we just use this?" six months later.
  3. You know the widget contract. Read the manager, the base widget class, the handlers, and the views directory first. The whole API surface is small; getting the contract into your head takes an hour, not a day.

Where to look first, in order:

  • The service provider — what it binds, what it publishes, whether it auto-discovers or must be registered manually.
  • The widget manager — the register/resolve/render triad. This is the piece you'll actually touch every day.
  • The handler(s) — how a named area is resolved and ordered.
  • The packaged Blade views — tells you the expected view namespace and what data a widget receives.

First use case: a placeholder area with one widget. Don't start with a dashboard. Start with a single named area in a layout (dashboard.sidebar, say), register one widget into it from a service provider, and render the area in the Blade layout. If that renders, the plumbing is sound and everything else is scale. If it renders an empty string, stop and fix that before writing widget number two — see the gotchas section, because empty-string rendering is the signature failure and it will not throw.

Half-day gate: constraint compatibility, boots under testbench, sample widget renders end to end, license and last-commit date checked. Any failure ends the evaluation — do not push through it, because there is no community to fall back on.

Implementation Patterns

The package implements a small, legible contract. Internalize it and everything else follows: id, area, order, visibility predicate, render callable (optionally with cache TTL). Every pattern below is a variation on that shape.

Define widgets as classes, not closures. Closures are fine for a spike, but a class gives you a name to log, a place to put constructor-injected dependencies, and something a test can instantiate directly. Keep the widget class thin — it should resolve a view model and hand off to Blade, nothing more. The view is where the markup lives; the class is where the data comes from.

Register from the module that owns the widget, not from the host app. The entire value of a widget layer is that the module contributing UI owns its own registration. Put registration in each package's service provider (or Orchestra extension bootstrap), gated behind a config flag so the host can disable a module's widgets without removing the module. A central WidgetServiceProvider that imports every module's widgets is the anti-pattern this package exists to prevent — resist it.

Render into named positions, never into specific widgets. Layouts should contain area calls and nothing else. The moment a layout references a widget by name, you've hard-coded the dashboard back into the theme and lost the abstraction. Areas are the public contract; widgets are the implementation detail.

{{-- resources/views/layouts/dashboard.blade.php --}}
<div class="grid">
    <aside>{!! widget('dashboard.sidebar') !!}</aside>
    <main>{!! widget('dashboard.main') !!}</main>
</div>

Use order for intent, not for pixel-perfect layout. Order values should express "critical above the fold" vs. "supplementary," not a full sort of everything. If two widgets need a deterministic relationship, give them clearly spaced order values (10, 20, 30) so a third party can slot in at 15 without a renumbering exercise. Equal order values with stable resolution are a coin flip the day you change the registration sequence.

Gate by permission at the registry, not in the widget's own view. The visibility predicate belongs in the registration, so the widget never renders for a user who can't see it — not even into a hidden div. Pushing the check into the Blade file means the widget still resolves, still runs queries, and still leaks its existence in the HTML. With Orchestra's ACL this is native; on a first-party port it's one closure.

Cache the resolved registry per request; cache widget output per widget. Resolution is cheap-ish but not free (container lookups, class string checks), and dashboard pages stack up. Resolve each area once per request and hold it. For output, per-widget TTLs beat one page-level cache — a slow-changing "recent signups" count can live five minutes while a "current user's tasks" widget renders live. Never cache a widget whose output is user-specific without folding the user id into the cache key.

Lazy-render anything that queries more than once. Widget rendering is view-layer work, so the real cost is N+1 queries inside individual widgets, not the registry. Two viable patterns: render a shell in the layout and fetch the widget body via an AJAX endpoint, or defer the expensive widgets behind a tab. Instrument the dashboard page before it becomes a page-load incident — a dozen widgets each doing two queries is twenty-four round trips the registry will happily orchestrate for you.

Test the registry, not just the widgets. Two tests earn their keep: (a) every registered widget resolves to a class and a view that exist, and (b) rendering each area produces non-empty output for a known user. Both are cheap, both catch the most common refactor breakage, and both belong in CI. A widget that silently renders "" passes a 200 OK smoke test and fails your users.

Extension points worth knowing. Subclassing or decorating the manager is the sanctioned way to add cross-cutting behavior — timing, logging, a development-mode banner listing every registered widget and its area. That dev-mode overlay is the single highest-leverage thing you can build on top of this package: it turns "why is my widget not showing" from a debugging session into a glance.

Gotchas and Tips

The signature bug is "renders empty string." Nine times out of ten it's an undeclared area, a registration that never ran (provider not booted, config flag off), or an order value that put the widget somewhere you aren't looking — not a broken render method. Before you debug the widget, dump the registry contents for that area. Put this in the runbook.

Silent failure is structural, not incidental. This package will not throw when a widget is misconfigured in most cases — it just contributes nothing. Every widget() call in a layout should be surrounded by a @if in development, or paired with a dev-mode overlay, so an empty area is visible rather than merely absent.

View namespace drift after a rename is the second-most-common breakage. Widget views are conventionally namespaced by package; rename the package or move the views directory and registration still succeeds while rendering fails. Add an assertion in your registry test that every widget's view actually exists — it's one line and it catches every rename.

Equal ordering is non-deterministic across refactors. Registration order is an implementation detail you do not control once multiple modules register. Space your order values and don't rely on declaration sequence.

Expect Laravel-upgrade friction and budget for it. Code of this vintage commonly depends on helpers that have since been removed (array_get, str_*). If you adopt, vendor the source, pin to a major, and add an upgrade gate to CI. Treat it as internal code with a public upstream — because with zero dependents and no patch stream, nobody else is going to fix it.

The learning tax is Orchestra, not the widget API. A competent Laravel engineer is productive in the registry in a day. Understanding extensions, memory config, and ACL enough to debug a widget that doesn't appear takes a week. That cost is invisible in a ticket estimate and belongs in the adoption decision.

Watch for namespace and container collisions. If you're also pulling orchestra/support and friends, PSR-4 conflicts and service-binding collisions show up during upgrades, not during install. A Composer constraint deadlock during a Laravel bump is the most probable blocker, and the cheapest to discover early.

Onboarding risk is real and worth naming. No documentation, no answer ecosystem, no hiring pool. Your support model is "one or two people who have read the source." Write internal docs and do a code walkthrough before anyone else touches it — the bus factor here is the actual risk, not the code.

Keep the option to reimplement open. The contract is roughly a day or two of work in a first-party service provider plus Blade components: a Widget interface, a registry bound in the container, a renderer, permission checks, and per-widget caching behind a feature flag. If you find yourself patching the package more than twice in a quarter, that's the signal — port the shape, drop the dependency, and stop paying the maintenance line.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core