tanthammar/livewire-window-size
Livewire Window Size exposes the browser's window dimensions and responsive breakpoints to your Livewire components, letting you react to resizes and adapt layouts or behavior server-side with minimal JavaScript. Easy to install and use.
Architecture fit. This is a narrow, single-purpose presentation-layer package: Blade directives plus PHP helpers that let a Livewire component decide what markup to emit based on the browser viewport. It is not infrastructure, not a data-layer concern, and it has no opinion about domain logic. That makes it architecturally harmless and easy to isolate — but also means its value is entirely contingent on one design question: do we actually need to not send markup, or do we just need to not show it?
The package inverts the normal stateless request/response contract. The server cannot know the viewport on the first request, so the library must establish a client→server feedback loop (a cookie, a request header, a Livewire property persisted across requests, or a re-render triggered by Alpine on boot). Everything good and bad about adopting it follows from that loop. If your app is already Livewire + Alpine-heavy, this is a natural extension of the rendering model. If you are on plain Blade, Inertia, or a JS SPA, it is a foreign body.
Integration feasibility. Mechanically trivial. Composer require, publish config if any, use the directives in Blade, call the helpers in PHP. Expect hours, not days, for a developer who already knows Livewire. The real effort is not integration — it is the semantic work: choosing which content is genuinely viewport-dependent, defining the breakpoint buckets, and deciding what the first paint shows.
One correction to the prior assessment: 4.0.0 did not work. mountHasBreakpoints() read a config key that does not exist, so the breakpoint set was always empty; getBreakpoint() was declared : array while returning an int, so every call raised a TypeError. Every path through the package was dead in 4.0.0. The assessment below therefore describes 4.0.1 and later only — any prior evaluation of "does this work" is void, and any lockfile still pinning 4.0.0 is running a package that silently renders its default branch forever.
Technical risk. Ranked, because these are not equivalent:
wire:navigate — that no user has hit yet because there are no users. This is now the strongest argument for the spike and for the fork contingency, not a footnote to it.Key questions to answer before approval.
Sec-CH-Viewport-Width / Client Hints, or is it cookie-only?wire:navigate prefetch, back/forward cache, and multi-tab?breakpoints.window-width) suggests the config schema moved, and a silent schema rename is exactly the class of change that breaks integrators.getBreakpoint()'s signature changed from : array to : int. Nothing could have depended on the old behaviour because it always threw, but any internal subclass, mock, or static-analysis baseline we write must target the new signature.Stack fit. Green light only for Laravel apps already running Livewire 3+ with Alpine bundled. Amber for apps that are Livewire-adjacent (a few islands). Red for anything without Livewire — introducing a reactive framework to conditionally render a nav bar is not a trade you want to defend in a design review.
There is also a governance cost: most Laravel shops already express responsiveness in Tailwind or Bootstrap breakpoints. This package creates a second breakpoint system, defined in PHP, that can drift from the CSS one. Mandate a single source of truth (one config file consumed by both) or you will be reconciling them within two quarters. The 4.0.1 bug is a live demonstration of this failure mode — the code and the config disagreed, and nothing detected the disagreement.
Migration path. Do not retrofit existing responsive layouts. Use it forward-only, on net-new surfaces where shipping hidden DOM is a genuine cost: heavy mobile-only components, large image variants, charts that should not be mounted below a threshold. Pilot on one low-risk, non-critical-path surface, instrument CLS/INP and cache hit rate for two weeks, then expand or retire. Treat "retire" as a live option — removal is a one-line composer command.
Compatibility checklist. Pin the exact version, and pin >=4.0.1 explicitly — a caret constraint resolved before this release may leave you on a build where nothing works. Confirm the Livewire constraint in composer.json matches your installed major; confirm PHP version support; verify no Alpine plugin conflicts (@alpinejs/* collisions are a common source of silent failure); confirm graceful degradation with JS off; confirm interaction with your cache layer and Vary headers; confirm the published config file actually contains breakpoints.window-width and that it resolves at runtime.
Sequencing.
getBreakpoint() returns a value and that mountHasBreakpoints() is non-empty. That second assertion is now mandatory: it is the cheapest possible check that the release is not another 4.0.0.Vary policy for affected routes.Maintenance. Low ongoing effort but non-zero ownership. The codebase is small and MIT-licensed, which makes forking or vendoring genuinely viable — budget a half-day to vendor it into packages/ if the maintainer goes quiet, and treat that as the contingency rather than a bridge you'll cross later. This release moves that contingency closer to the default plan: a one-maintainer project that shipped a wholly non-functional release is one we should expect to patch ourselves. The other maintenance obligation is tracking Livewire majors, an external event you do not control.
Support. With 23 stars and zero dependents there is no community corpus — no Stack Overflow answers, no blog posts, no prior art. Every question routes to the maintainer or your own team. Internal documentation is not optional; it is the only documentation. Conversely, onboarding cost is concentrated in the mental model (the first-paint problem, the cache implications) rather than the API, which is small enough to read end-to-end in an afternoon — and should be read end-to-end, because the 4.0.1 defects were visible by inspection to anyone who compared the config file to the method bodies. Make that read part of onboarding, not just the API docs.
Scaling. Request-level cost is negligible — reading a cookie or header is free. The scaling pressure lands on your edge: cache key cardinality grows with viewport buckets, and resize-triggered Livewire updates are network round trips, so a user dragging a window can generate a burst. Confirm debouncing. Keep buckets coarse; there is no justification for more than three.
Failure modes to design against.
Ramp-up. Half a day for a mid-level Laravel developer to use the API competently; one to two days to use it safely, meaning to understand the first-paint and caching semantics well enough not to ship a regression. Add mandatory review from whoever owns the edge/CDN layer — that is where the incidents will originate, and that person is typically not the one writing the Blade directives. Add, for the first adoption only, whoever owns dependency hygiene: they should confirm the pin, read the diff between the release we adopt and the release before it, and decide whether we vendor.
TPM position. The capability is real but narrow: it is for teams who must avoid sending markup, not merely hiding it. For the majority of responsive requirements, CSS media queries remain cheaper, cache-friendly, better understood, and better indexed. 4.0.1 resolves the "it is broken" blocker and adds no capability — the first-paint and caching questions that decide this project are untouched, and the release has lowered our confidence in upstream quality while raising the value of our own test coverage. Approve conditionally, unchanged in shape but stricter in terms: Livewire + Alpine already in place, a specific payload or mounting problem to solve, a measured pilot, a named owner for the caching policy, an exact pin to >=4.0.1 with our own assertion test, and a fork budget already allocated. Absent those, decline and revisit if adoption signals improve — noting that adoption signals have not improved as a result of this release, only correctness.
How can I help you explore Laravel packages today?