skip to content

In Blade, how do @push, @prepend, @stack and @pushOnce let each university course page add its own scripts to a shared layout?

level: middleimportance: must knowfreq 48%

answer

  1. named stacks collected during rendering
  2. @stack prints what was collected so far
  3. @prepend goes ahead of pushed content
  4. @pushOnce id is per directive, or yours
  5. state flushed after the top-level render

basics

~20 s

A page or component adds markup to a named stack with @push('scripts'), and the layout prints everything collected with @stack('scripts'). @prepend puts content ahead of pushed content, and @pushOnce adds a block only once per render.

solid answer

~40 s

Stacks are named buckets on the view factory. A course page writes `@push('scripts') <script src="/js/campus-map.js"></script> @endpush`, the layout prints `@stack('scripts')`, and every pushed block comes out there; `@prepend` blocks come out ahead of all pushed ones. It works because a page's content, whether an `@extends` child or the slot of a layout component, runs before the layout's markup. The catch is timing: `@stack` prints what has been collected *when it runs*, so a partial the layout includes after the stack pushes too late, which is why `@stack('scripts')` usually sits just before `</body>`. `@pushOnce` stops a component rendered thirty times from pushing thirty copies; without a second argument its identity is per directive, so two templates pushing the same library need a shared id like `@pushOnce('scripts', 'campus-map')`. There are also `@pushIf`, `@prependOnce` and `@hasstack`.

code

html · 15 lines
html
<!-- resources/views/components/layout.blade.php -->
<html>
<body>
    {{ $slot }}
    @include('partials.footer')

    @stack('scripts')
</body>
</html>

<!-- resources/views/components/course-card.blade.php -->
<article>{{ $course->name }}</article>
@pushOnce('scripts', 'campus-map')
    <script src="/js/campus-map.js"></script>
@endPushOnce

go deeper

for a junior

Recall the pair: pages @push('scripts'), the layout prints @stack('scripts'). Know that @prepend puts content first and @pushOnce avoids duplicates.

for a middle

Explain why pushes arrive: child and slot content run before the layout prints @stack. Explain how @pushOnce decides what is a duplicate.

for a senior

Diagnose the missing script from a partial included after @stack, the double-loaded library from two @pushOnce directives, and script-order bugs between prepends and pushes.

for a principal

Decide what belongs in stacks versus the bundler's entry points; a sprawl of per-page script tags is a front-end architecture smell worth a team rule.

## What a stack is A **stack** in Blade is a named bucket of HTML held by the view factory while a response renders. Any view taking part in the render can add to it, and a layout prints it wherever it wants. The classic use is per-page assets: a university site's shared layout should not load the campus-map script on every page, but the pages that show a map need it. - `@push('scripts') ... @endpush` appends a block to the `scripts` stack. - `@prepend('scripts') ... @endprepend` adds a block that comes out **ahead of** all pushed blocks. - `@stack('scripts')` prints the stack's collected content at that spot. - `@pushIf($condition, 'scripts') ... @endPushIf` pushes only when the condition is true. - `@hasstack('scripts') ... @endif` wraps markup that should appear only when the stack is not empty. `@push` itself prints nothing where it is written; the block only appears where `@stack` prints it. ## Why pushes reach the layout A stack is printed exactly once, at the moment `@stack` executes, and it contains whatever has been collected by then. Pages reach the layout's stack because of render order: 1. With template inheritance, the child's code runs first and the layout is rendered from the child's compiled footer. 2. With a layout component, the markup between `<x-layout>` tags is captured into the slot before the component's own template renders. Either way, the page's `@push` blocks execute before the layout reaches `@stack`. ## The timing trap The same rule bites inside the layout itself. Suppose the layout prints `@stack('scripts')` in `<head>` and later, in `<body>`, includes a footer partial that pushes a cookie-banner script. The stack has already been printed when the footer runs, so the banner script **never appears**, and no error is raised. The fixes: - print `@stack('scripts')` near the end of `<body>`, after the includes that push to it; - or keep head-bound assets in a separate stack (for example `styles`) that only pages and slot content push to. ## Ordering | Source | Where it lands in `@stack` output | |---|---| | `@prepend` blocks | first, ahead of every pushed block | | `@push` blocks | after the prepended blocks, concatenated in collection order | | nothing pushed | `@stack` prints nothing; `@hasstack` is false | If script order matters, for example a library before the code that uses it, `@prepend` the library or push it from a place that is guaranteed to run first. ## @pushOnce and @prependOnce Components are where duplicate pushes happen. A `course-card` component that shows a small map and pushes the map script would push it once per card, so a listing of thirty courses loads the script thirty times. `@pushOnce('scripts') ... @endPushOnce` fixes that: - Without a second argument, the compiler generates a **UUID for that directive at compile time**, so the block is pushed once per render no matter how many times that template renders. - That identity is per directive, not per content. If `course-card` and `event-card` both push the same map library, each directive has its own UUID and the library is pushed twice. Give both the same explicit id: `@pushOnce('scripts', 'campus-map')`. - `@prependOnce` is the same thing for prepends. ## A worked university example A course page for Marine Biology shows a campus map and a timetable widget: 1. The page wraps itself in `<x-layout>` and, inside the slot, does `@push('scripts')` for the timetable script only it needs. 2. It renders a `course-card` component for each related course; each card uses `@pushOnce('scripts', 'campus-map')` for the map library. 3. The layout prints the slot, then includes the footer partial, and prints `@stack('scripts')` last, just before `</body>`. The response contains the timetable script once, the map library once, and any script the footer pushed, and pages without maps load neither library. ## Lifetime Stacks, sections and the "rendered once" markers live on the view factory only for the current top-level render. When the outermost view finishes rendering, or throws, the factory flushes that state, so pushes never leak into the next response or into a view rendered after that one finishes.

  • Two different Blade components both use @pushOnce('scripts') around the same script tag. Why does the script still appear twice?
    Without a second argument, `@pushOnce` identifies itself by a UUID the compiler generates for that directive, so each component's directive has its own identity. Blade deduplicates by that id, not by comparing the pushed HTML. Passing the same explicit id in both, for example `@pushOnce('scripts', 'campus-map')`, makes the second one a no-op.
  • How would you render a wrapper element only when a Blade page actually pushed something to a stack?
    Wrap it in `@hasstack('modals') ... @endif`. The directive compiles to a check that the stack is not empty, so the layout prints the wrapper and `@stack('modals')` only when some page or component pushed or prepended to `modals`. Like `@stack` itself, it sees only what has been collected before it runs.

saying these in an interview costs you the question

  • A @push anywhere in the response reaches @stack, even after the stack was printed
  • @pushOnce without an id deduplicates identical script tags across templates
  • @prepend inserts content at the top of the page's <head>
  • @push prints its block where it is written as well as in the stack
  • Pushed content persists between requests like a cache