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

View Laravel Package

orchestra/view

Orchestra View provides view-layer utilities for Laravel applications, extending the framework's view factory with helpers for resolving, composing, and managing templates within the Orchestra ecosystem. It keeps theme and template handling lightweight and consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

orchestra/view is not a templating engine and it is not a theming product — it is a swap of Laravel's native view machinery for an ordered, override-friendly variant. Day to day you write ordinary Blade. What changes is where the file comes from: Orchestra replaces view.finder with Orchestra\View\FileViewFinder, a finder that resolves templates across themes, packages, and modules in a priority order you control, and it fires lifecycle events around composition and rendering. The correct mental model is "native Blade plus a stack of search roots that providers push into," nothing more.

Install and confirm wiring. Composer install should be enough where package discovery is enabled:

composer require orchestra/view

If discovery is off, register the provider explicitly and gate it behind a config flag so you have a one-line fallback to native view services:

'providers' => [
    Orchestra\View\ViewServiceProvider::class,
],

Read in this order before writing anything:

  1. Orchestra\View\ViewServiceProvider — register() and boot(). Note which container keys are rebound (view, view.finder, the engine resolver) and in what order. This is the entire integration surface; there is no other API.
  2. Orchestra\View\FileViewFinder — confirm the methods you will actually call: addLocation(), prependLocation(), addNamespace(), prependNamespace(), addExtension(), getPaths(), getHints().
  3. config/view.php — still standard Laravel. Orchestra layers on top; it does not replace paths, compiled, or view.cache.

First real use case: package views the host app can override without publishing. This is the highest-leverage pattern the package enables and the one you will reach for most.

// Package service provider boot()
public function boot()
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'acme');
}
// Host application service provider boot()
public function boot()
{
    $this->app->extend('view.finder', function ($finder) {
        $finder->prependNamespace('acme', resource_path('views/vendor/acme'));
        return $finder;
    });
}

view('acme::dashboard') now resolves from resources/views/vendor/acme/dashboard.blade.php first, then falls back to the vendor copy. Ship that override directory as the documented contract and consumers never touch your code.

Implementation Patterns

Control ordering explicitly — it is the whole feature. addLocation() appends a root; prependLocation() puts it at the front so it beats resources/views. Append for module/generated views, prepend for the active theme:

$this->app->extend('view.finder', function ($finder) {
    $finder->prependLocation(base_path('themes/active/views'));
    $finder->addLocation(base_path('modules/Blog/Resources/views'));
    return $finder;
});

Register theme paths in exactly one place — a single theme service provider — not scattered across feature providers. Multiple prepends produce a stack you have to reason about at debug time, and "which provider ran first" becomes the bug.

Treat every package and module as a namespace. blog::post.index, admin::layouts.app, never flat post.index. It costs nothing, removes collisions between packages, and gives consumers a named override directory. Flat names in a multi-package app are a latent conflict waiting for the second package.

Extend the finder, never rebind it. The single most common integration bug:

// Good — composes with theme/extension providers
$this->app->extend('view.finder', fn ($finder) => $finder->prependLocation($path));

// Bad — silently discards every path registered by other providers
$this->app['view.finder'] = new MyFinder($this->app['files'], []);

Your provider may boot before or after orchestra/theme and friends. extend() composes; assignment destroys.

Composers over View::share(). The factory keeps the normal composer API and class composers resolve from the container, so constructor injection and unit tests work unchanged:

View::composer(['admin::*', 'layouts.*'], function ($view) {
    $view->with('currentUser', auth()->user());
    $view->with('locale', app()->getLocale());
});

View::composer('admin::dashboard', DashboardComposer::class);

Use View::creator() instead of composer() when data must exist before the view is instantiated — for example a @php block at the top of the template that depends on it. Composer callbacks run after instantiation.

Hook the render lifecycle for cross-cutting output. Orchestra fires events around composition and rendering, which is the right place for decoration that should not live in every template — analytics snippets, CSRF meta tags, breadcrumb injection. Bind a listener rather than wrapping view()->render().

Event::listen('composing: admin::*', function ($view) {
    $view->with('breadcrumbs', app(BreadcrumbRegistry::class)->all());
});

Verify exact event names against the provider source. Keep listeners cheap and side-effect free — they run on every matching render, including mail and error pages.

Register custom syntax through the engine resolver, not a new factory.

$this->app['view.engine.resolver']->extend('md', function () {
    return new MarkdownEngine($this->app['files']);
});

Then addExtension('md', 'blade') on the finder gets the file and the resolver picks the class.

Namespaced fixtures in tests. Register a test namespace rather than committing view files:

view()->addNamespace('test', __DIR__.'/fixtures/views');

Assume sibling providers are present. In a full Orchestra app, orchestra/extension and orchestra/theme push into the same finder. Your registrations must be idempotent and order-independent — no assumptions about who booted first, no duplicate prepends on repeated boot.

Gotchas and Tips

  • Stale compiled views after moving a theme. Compile paths are hashed from the view name, not the resolved path, so two themes with identically named templates can collide. Run view:clear first whenever you see a layout from the wrong theme.
  • Register in boot(), not register(). extend('view.finder', ...) in register() can run before the binding exists — you either throw or resolve a fresh finder you then mutate pointlessly.
  • Config caching freezes config/view.php. Runtime-computed paths (active theme, tenant) must be registered through the finder in boot(), never baked into config, or config:cache will pin the wrong theme.
  • Prepend order is a stack — last call usually wins. If your theme override is losing to a package's own path, the fix is call ordering, not a different API.
  • View::exists() and @includeIf are finder-dependent. A path added after the first exists() check yields a surprising false. Never memoize existence checks across requests in Octane or queue workers without an invalidation path.
  • Wildcard composers on * fire on mail, notifications, and error pages. Scope to layouts.* or admin::*. A composer that hits the database on the exception page turns a 500 into a fatal.
  • Debug the resolution, not the template. When a view will not render, dump the finder before blaming Blade:
dump(app('view.finder')->getPaths(), app('view.finder')->getHints());

Then read the find() exception — it lists the searched paths and is the ground truth for whether your registration actually landed.

  • Do not let two packages own the view layer. If orchestra/theme is installed, do not register an independent theme resolver; you get duplicated path entries and unpredictable precedence.
  • Blade directives are per-compile. A Blade::directive() registered inside a conditional boot() branch leaves partially compiled views across deploys. Register unconditionally or not at all, and clear compiled views after changing them.
  • Version and compatibility gate. ^6.0 targets Laravel 8 — the release trail stops there. Validate package discovery, config caching, view caching, mail rendering, and the test environment against your real Laravel/PHP matrix before trusting it.
  • Assume you own it. Zero stars, zero dependents, no community bug reports, no evidence of production validation. Budget an internal owner, a CI compatibility matrix, and a written list of the behaviors you actually consume (namespace override, theme precedence, lifecycle events). That list is your replacement spec.
  • The three extension points cover almost everything: a FileViewFinder subclass bound via extend(), an engine on view.engine.resolver, and lifecycle event listeners. Reach for those before forking.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums