dimsemenov/magnific-popup
Magnific Popup is a fast, lightweight, responsive lightbox plugin for jQuery that displays images, galleries, videos, iframes, AJAX and inline content with smooth CSS animations, a modular API, and retina-ready mobile support.
Magnific Popup is a front-end jQuery plugin, not a Laravel package — there is no service provider, no config file, and no PHP surface. Treat it as a static asset dependency and budget your integration work in resources/js and Blade only.
npm i jquery magnific-popup
window before importing the plugin. The plugin is a UMD bundle that augments $.fn, so import order matters:// resources/js/app.js
import $ from 'jquery';
window.$ = window.jQuery = $; // needed for inline Blade scripts + Alpine
import 'magnific-popup';
import 'magnific-popup/dist/magnific-popup.css'; // verify exact filename in node_modules/magnific-popup/dist
href (or data-mfp-src) as the target:<a href="{{ Storage::url($photo->path) }}"
class="js-lightbox"
title="{{ $photo->caption }}">
<img src="{{ Storage::url($photo->thumb_path) }}" alt="{{ $photo->caption }}">
</a>
delegate so items added by Livewire, infinite scroll, or pagination keep working:$('.js-gallery').magnificPopup({
delegate: 'a.js-lightbox',
type: 'image',
gallery: { enabled: true, preload: [0, 2] },
mainClass: 'mfp-fade',
removalDelay: 200,
});
Where to look first: the official docs (dimsemenov.com/plugins/magnific-popup) are still the best reference for the options object; node_modules/magnific-popup/dist for shipped CSS/JS filenames; and the data('magnificPopup') instance in devtools for runtime state. First use case: an image gallery on a resource index/show page is the canonical fit — five minutes of work, no server changes.
AJAX popups that load a Blade partial. This is where it pays off in Laravel: a "Quick create" or "Edit" form rendered server-side into the lightbox.
// PhotoController@edit
if ($request->ajax()) {
return view('photos._form', ['photo' => $photo]); // no layout
}
$('.js-modal-form').magnificPopup({
type: 'ajax',
midClick: true,
closeOnBgClick: false,
ajax: {
settings: {
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content },
dataType: 'html',
},
},
callbacks: {
ajaxContentAdded() {
this.content.find('form').on('submit', submitQuickForm);
},
},
});
Then on success call $.magnificPopup.close() and refresh the listing — window.location.reload(), an axios table reload, or Livewire.dispatch('photo-saved'). Note $request->ajax() depends on the X-Requested-With header; Magnific sets it by default, but pass it explicitly if you have custom middleware.
Inline markup instead of a remote fetch. Wrap a <template> or hidden <div id="inline-popup"> and use type: 'inline' with data-mfp-src="#inline-popup". Good for confirmations and short Alpine-driven forms where a round trip is wasteful.
Video and iframe content. type: 'iframe' accepts a patterns object for YouTube/Vimeo URL rewriting; iframe.markup lets you replace the wrapper with your own responsive-embed classes so it plays nicely with Tailwind's aspect utilities.
Reusable JS module. Keep the options in resources/js/lightbox.js and export an initLightbox(root = document) function; call it on DOMContentLoaded, livewire:navigated, and after Livewire.hook('morph.updated'). Guard with if (!el.data('magnificPopup')) to avoid double-initialising.
Analytics and side effects via callbacks. open, close, change, and elementParse fire with this bound to the instance (this.currItem, this.content). Use change to log gallery navigation — cleaner than wiring click handlers on every thumbnail.
window.jQuery.fn.magnificPopup exists in the console before debugging further. Add resolve: { dedupe: ['jquery'] } in vite.config.js.magnific-popup.css after your Tailwind layer, and expect to touch .mfp-* rules. Also raise z-index if you use Bootstrap/Tailwind modals: .mfp-bg, .mfp-wrap { z-index: 9999; }.x-data roots, and delegated listeners inside that subtree can lose bindings or re-initialise on close. Prefer type: 'ajax' for anything stateful.mainClass alone does nothing visible; you need removalDelay to match your CSS transition, otherwise the popup closes instantly.delegate, new elements work automatically — without it, you must re-init after every render. This is the single most common Livewire bug report.aria-modal, no focus restoration. Add role="dialog"/aria-modal="true" via callbacks.open, store document.activeElement, and restore it in close. If a11y is a hard requirement, evaluate GLightbox or Fancybox instead.type: 'iframe' (open-redirect/XSS), and only load popup content from your own routes.style attributes and data-URI spinners; a strict style-src will break positioning and the loading indicator. Budget for style-src 'unsafe-inline' or hash-based exceptions.fixedContentPos: false and overflowY: 'auto'..mfp-ready. Assert ->waitFor('.mfp-ready') before touching .mfp-content children, and remember the content is outside your page root.$.magnificPopup.registerModule(name, { options, proto: {...} }) is the documented way to add a content type or behaviour; subclass an existing module rather than monkey-patching callbacks.How can I help you explore Laravel packages today?