Reader Stacks

Sharing Data With Every Blade View in Laravel (View Composers)

Passing the same data (a site name, a list of categories for a navbar) into every single controller method that returns a view doesn't scale — View::share() and view composers exist to avoid exactly that repetition.

Data that a layout or partial needs on essentially every page — a navigation menu's categories, site-wide settings, the current user's notification count — shouldn't have to be manually fetched and passed into every single controller method that happens to return a view. Laravel has two mechanisms specifically for this, at different levels of granularity.

1. The problem this solves

// Repeated in every single controller method, just to populate the navbar
public function index()
{
    $categories = Category::all();
    return view('products.index', compact('categories', /* ...actual page data... */));
}

public function show(Product $product)
{
    $categories = Category::all(); // same fetch, duplicated again
    return view('products.show', compact('categories', 'product'));
}

2. View::share() — truly global data

// A service provider's boot() method, commonly AppServiceProvider
use Illuminate\Support\Facades\View;

public function boot(): void
{
    View::share('appName', config('app.name'));
}

View::share() makes a value available in every view rendered anywhere in the app, without it needing to be passed explicitly at all — appropriate for genuinely static, rarely-changing values like an app name or a build version, but not a good fit for anything requiring a database query, since it runs unconditionally on every single request regardless of whether the current view actually needs it.

3. View composers — scoped to specific views, and lazy

// A service provider's boot() method
use Illuminate\Support\Facades\View;

public function boot(): void
{
    View::composer('layouts.navigation', function ($view) {
        $view->with('categories', Category::all());
    });
}

A view composer's callback only runs when the specified view (layouts.navigation here) is actually about to be rendered — this is the meaningful advantage over View::share() for anything involving a real query: the Category::all() call only executes on requests that actually render this specific partial, not on every request across the whole app.

4. Composing multiple views, or every view, with one composer

View::composer(['layouts.navigation', 'layouts.footer'], function ($view) {
    $view->with('categories', Category::all());
});

View::composer('*', function ($view) {
    // runs for every view — use sparingly, same caveat as View::share() for expensive data
});

5. Using a dedicated composer class for anything non-trivial

class NavigationComposer
{
    public function compose(\Illuminate\View\View $view): void
    {
        $view->with('categories', Category::where('is_active', true)->orderBy('name')->get());
    }
}
View::composer('layouts.navigation', NavigationComposer::class);

For anything beyond a one-line query, a dedicated composer class keeps the logic out of a service provider's boot() method — easier to test in isolation, and keeps the service provider itself focused on registration rather than containing real business logic inline.

6. When this is the wrong tool

Data specific to one particular page (a single product's details, a specific order's line items) should still be passed explicitly from that page's own controller method — view composers and View::share() are specifically for data genuinely needed across many otherwise-unrelated views (typically layout and navigation elements), not a general substitute for passing page-specific data the normal way.

Topics: Developer Productivity