lightGallery
One gallery, four packages with the same features and settings:
| Stack | Package | Plugins import path |
|---|---|---|
| Vanilla JS / TypeScript | lightgallery | lightgallery/plugins/<name> |
| React | @lightgallery/react | @lightgallery/react/plugins/<name> |
| Vue 3 | @lightgallery/vue | @lightgallery/vue/plugins/<name> |
| Angular | @lightgallery/angular | @lightgallery/angular/plugins/<name> |
Plugin names: thumbnail, zoom, video, autoplay, fullscreen, share,
hash, rotate, pager, comment, mediumZoom, relativeCaption,
vimeoThumbnail, originCrop, justified. Each is a separate entry — import
only what the project uses.
Default recipe. Unless the user asks for something else, every image
gallery gets the zoom and thumbnail plugins, their two CSS files, and
lgSize: 'WIDTH-HEIGHT' (data-lg-size in markup) on every image item.
lgSize is the natural size of the full image; it drives the
zoom-from-origin animation, where the slide opens from the clicked
thumbnail and closes back to it. Without it the slide fades in. Read the
dimensions from the image files, the image optimizer or the CMS rather than
dropping the field. Leave a plugin out only when told to.
Two rules that prevent most problems
- CSS is a consumer import in every stack. The framework packages ship
no CSS. Import
lightgallery/css/lightgallery.cssplus one file per plugin (lightgallery/css/lg-thumbnail.css,lg-zoom.css, …), orlightgallery/css/lightgallery-bundle.cssfor everything. - Plugins are opt-in. A feature only exists when its plugin is passed
(
plugins: [...],plugins={[...]},:plugins="[...]",[features]="[withX()]"). Its settings are ignored otherwise.
Vanilla JavaScript
jsimport lightGallery from 'lightgallery'; import lgThumbnail from 'lightgallery/plugins/thumbnail'; import lgZoom from 'lightgallery/plugins/zoom'; import 'lightgallery/css/lightgallery.css'; import 'lightgallery/css/lg-thumbnail.css'; import 'lightgallery/css/lg-zoom.css'; const gallery = lightGallery(document.getElementById('gallery'), { plugins: [lgZoom, lgThumbnail], speed: 400, });
Markup mode: the container holds anchors; each anchor's href (or
data-src) is the full image, the inner <img> is the thumbnail, and
data-lg-size="WIDTH-HEIGHT" is the full image's natural size for the
zoom-from-origin animation. Captions come from data-sub-html. If items are
not direct children, set selector. Dynamic mode:
dynamic: true, dynamicEl: [{ src, thumb, lgSize, subHtml }] and call
gallery.openGallery(index).
Methods: openGallery(index), closeGallery(), refresh() after the
markup changed, destroy(). Events fire on the container element
(lgBeforeOpen, lgAfterSlide, …) with data in event.detail; attach
listeners before initializing.
React
tsx'use client'; // Next.js App Router: the gallery is a client component import { LightGallery, LightGalleryItem } from '@lightgallery/react'; import Thumbnail from '@lightgallery/react/plugins/thumbnail'; import Zoom from '@lightgallery/react/plugins/zoom'; // items: [{ src, thumb, alt, lgSize: '1600-1067' }, …] export function Gallery({ items }) { return ( <LightGallery plugins={[Zoom, Thumbnail]} thumbnail={{ animateThumb: true }}> {items.map((item) => ( <LightGalleryItem key={item.src} item={item}> <img src={item.thumb} alt={item.alt} /> </LightGalleryItem> ))} </LightGallery> ); }
Import the CSS in the root layout. Plugin settings are objects on props
named after the plugin (zoom={{ … }}, thumbnail={{ … }}); bare boolean
props are not valid. Callbacks are onAfterSlide, onBeforeOpen, …; the
imperative handle (ref) exposes openGallery, closeGallery,
goToSlide, nextSlide, prevSlide. Use either the controlled open
prop or the uncontrolled default for the component's whole lifetime, not
both. The package is SSR-safe; no dynamic(..., { ssr: false }) needed.
Vue 3
vue<script setup> import { LightGallery, LgItem } from '@lightgallery/vue'; import Thumbnail from '@lightgallery/vue/plugins/thumbnail'; import Zoom from '@lightgallery/vue/plugins/zoom'; const items = [{ src: '/photos/1.jpg', thumb: '/photos/1-thumb.jpg', alt: 'One', lgSize: '1600-1067' }]; </script> <template> <LightGallery :plugins="[Zoom, Thumbnail]" :thumbnail="{ animateThumb: true }"> <LgItem v-for="item of items" :key="item.src" :item="item"> <img :src="item.thumb" :alt="item.alt" /> </LgItem> </LightGallery> </template>
v-model:open controls the open state; events are emitted without the
on prefix (@after-slide). Custom icons: :icons="{ close: MyIcon }".
Angular
tsimport { Component } from '@angular/core'; import { LgGalleryComponent, LgGalleryItemDirective } from '@lightgallery/angular'; import { withThumbnail } from '@lightgallery/angular/plugins/thumbnail'; import { withZoom } from '@lightgallery/angular/plugins/zoom'; @Component({ selector: 'app-gallery', standalone: true, imports: [LgGalleryComponent, LgGalleryItemDirective], template: ` <lg-gallery [features]="features"> @for (item of items; track item.src) { <a [lgGalleryItem]="item"><img [src]="item.thumb" [alt]="item.alt" /></a> } </lg-gallery> `, }) export class GalleryComponent { items = [{ src: '/photos/1.jpg', thumb: '/photos/1-thumb.jpg', alt: 'One', lgSize: '1600-1067' }]; features = [withThumbnail({ animateThumb: true }), withZoom()]; }
Add the CSS files to angular.json styles or the root stylesheet.
Settings are inputs ([speed], [loop]); events are outputs without the
on prefix ((afterSlide)).
Settings worth knowing
mobileSettings: { controls, showCloseIcon, download }— a second settings object applied on small screens.strings— every UI label, merged per key; use it to localize.direction: 'rtl'(or'auto') mirrors the gallery; importlightgallery/css/lg-rtl.css.icons— inline SVG per icon name; state pairs (maximize/minimize,autoplayPlay/autoplayPause,fullscreen/fullscreenExit) need both.- Video: items with a YouTube/Vimeo/Wistia URL or
data-videoJSON render a poster and load the player on play (videoFacade). virtualizationfor galleries with hundreds of slides;scrubThumbnailsto scrub with the strip;hashDriverfor deep links.licenseKey— required for commercial use; the default key logs a warning.
Pitfalls
- No styles → the CSS import is missing (see rule 1).
- Thumbnails/zoom "not working" → the plugin is not in the plugins list.
- The slide fades in instead of opening from its thumbnail →
lgSize(data-lg-size) is missing or does not match the image's natural size. - "data-src is not provided" → the
selectordoes not match the anchors. - Slides added after init are not shown → call
refresh()(vanilla) or update the reactiveitems(frameworks). - React: switching between controlled and uncontrolled
openlogs an error. - Every symptom with its fix per package: https://www.lightgalleryjs.com/docs/troubleshooting/index.md
Reference
- Docs index for agents: https://www.lightgalleryjs.com/llms.txt
- Every setting with type and default: https://www.lightgalleryjs.com/docs/settings/index.md
- Events: https://www.lightgalleryjs.com/docs/events/index.md · Methods: https://www.lightgalleryjs.com/docs/methods/index.md
- Framework guides: /docs/react/index.md, /docs/vue/index.md, /docs/angular/index.md on the same host


