Lightbox
@reelkit/react-lightbox से बना फ़ुलस्क्रीन इमेज और वीडियो गैलरी lightbox कंपोनेंट।
फ़ीचर
इंस्टॉलेशन
npm install @reelkit/react-lightbox @reelkit/react lucide-reactstyles import करना न भूलें:
import '@reelkit/react-lightbox/styles.css';आइकन
डिफ़ॉल्ट कंट्रोल आइकन के लिए lucide-react इस्तेमाल करते हैं। अगर आप कोई और आइकन लाइब्रेरी पसंद करते हैं, तो अपने कंट्रोल देने के लिए renderControls और renderNavigation इस्तेमाल करें।
क्विक स्टार्ट
LightboxOverlay कंपोनेंट इमेज फ़ुलस्क्रीन में दिखाता है। LightboxItem objects का array पास करें और null हो सकने वाले index से दिखना नियंत्रित करें।
import { useState } from 'react';
import { LightboxOverlay, type LightboxItem } from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';
const images: LightboxItem[] = [
{
src: 'https://example.com/image1.jpg',
title: 'Sunset',
description: 'Beautiful sunset over the ocean',
},
{
src: 'https://example.com/image2.jpg',
title: 'Mountains',
},
];
function App() {
const [index, setIndex] = useState<number | null>(null);
return (
<>
{images.map((img, i) => (
<img
key={i}
src={img.src}
onClick={() => setIndex(i)}
/>
))}
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
/>
</>
);
}import { useState } from 'react';
import {
LightboxOverlay,
flipTransition,
lightboxFadeTransition,
lightboxZoomTransition,
slideTransition,
type LightboxItem,
} from '@reelkit/react-lightbox';
import type { TransitionTransformFn } from '@reelkit/react';
import '@reelkit/react-lightbox/styles.css';
const images: LightboxItem[] = [
{
src: '/cdn/samples/images/image-01.jpg',
title: 'Mountain River',
description: 'A beautiful mountain river flowing through the forest',
width: 1600,
height: 1000,
},
{
src: '/cdn/samples/images/image-02.jpg',
title: 'Snowy Peaks',
description: 'Majestic snow-capped mountains reaching for the sky',
width: 1000,
height: 1600,
},
{
src: '/cdn/samples/images/image-03.jpg',
title: 'Foggy Forest',
description: 'Misty morning in the dense forest',
width: 1600,
height: 900,
},
{
src: '/cdn/samples/images/image-04.jpg',
title: 'Ocean Waves',
description: 'Powerful ocean waves crashing against the rocky shore',
width: 900,
height: 1400,
},
{
src: '/cdn/samples/images/image-05.jpg',
title: 'Autumn Path',
description: 'A winding path through the autumn forest',
width: 1600,
height: 1067,
},
{
src: '/cdn/samples/images/image-06.jpg',
title: 'Coastal Cliffs',
description: 'Dramatic coastal cliffs overlooking the deep blue sea',
width: 1600,
height: 1067,
},
];
const transitions: { label: string; fn: TransitionTransformFn }[] = [
{ label: 'slide', fn: slideTransition },
{ label: 'fade', fn: lightboxFadeTransition },
{ label: 'flip', fn: flipTransition },
{ label: 'zoom-in', fn: lightboxZoomTransition },
];
export default function App() {
const [index, setIndex] = useState<number | null>(null);
const [transitionFn, setTransitionFn] = useState<TransitionTransformFn>(
() => slideTransition,
);
return (
<div style={{ padding: 16, background: '#f8fafc', minHeight: '100vh' }}>
{/* Transition picker */}
<div style={{ display: 'flex', gap: 6, marginBottom: 12 }}>
{transitions.map((t) => (
<button
key={t.label}
onClick={() => setTransitionFn(() => t.fn)}
style={{
padding: '6px 14px', borderRadius: 8, border: 'none', cursor: 'pointer',
fontSize: 13, fontWeight: 500,
background: transitionFn === t.fn ? '#6366f1' : '#e2e8f0',
color: transitionFn === t.fn ? '#fff' : '#334155',
}}
>
{t.label}
</button>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
{images.map((img, i) => (
<button
key={i}
onClick={() => setIndex(i)}
style={{
position: 'relative', aspectRatio: '4 / 3', borderRadius: 8,
overflow: 'hidden', border: 'none', padding: 0, cursor: 'pointer',
background: '#e2e8f0',
}}
>
<img
src={img.src}
alt={img.title}
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
/>
</button>
))}
</div>
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
transitionFn={transitionFn}
/>
</div>
);
}गैलरी को स्लाइडशो की तरह चलाने और हर फ़ोटो की मूल फ़ाइल सेव करने के लिए गैलरी स्लाइडशो रेसिपी देखें।
वीडियो स्लाइड (opt-in)
वीडियो सपोर्ट पूरी तरह opt-in और tree-shake होने लायक है — सिर्फ़ इमेज इस्तेमाल करने पर बंडल पर कोई अतिरिक्त भार नहीं पड़ता। useVideoSlideRenderer import करें और उसकी लौटाई values को LightboxOverlay से जोड़ें। Hook लोडिंग स्टेट, साउंड प्रबंधन और वीडियो lifecycle अपने-आप संभालता है।
import {
LightboxOverlay,
useVideoSlideRenderer,
type LightboxItem,
} from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';
const items: LightboxItem[] = [
{ src: '/photo.jpg', title: 'Photo' },
{
src: '/clip.mp4',
type: 'video',
poster: '/clip-thumb.jpg',
title: 'Video Clip',
},
];
function Gallery() {
const [index, setIndex] = useState<number | null>(null);
const isOpen = index !== null;
const { renderSlide, renderControls, SoundProvider } =
useVideoSlideRenderer(items);
return (
<SoundProvider>
{/* thumbnails… */}
<LightboxOverlay
isOpen={isOpen}
images={items}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
renderSlide={renderSlide}
renderControls={renderControls}
/>
</SoundProvider>
);
}यह कैसे काम करता है
- Hook
SoundProviderलौटाता है — mute/unmute काम करे, इसके लिए अपने overlay को इसमें लपेटें - स्लाइड सक्रिय होने पर वीडियो अपने-आप चलते हैं (डिफ़ॉल्ट रूप से muted)
- iOS पर आवाज़ बनी रहे, इसके लिए स्लाइडों के बीच एक साझा वीडियो एलिमेंट दोबारा इस्तेमाल होता है
- वीडियो स्लाइड पर reactive mute टॉगल वाला साउंड बटन अपने-आप दिखता है
type: 'video'के बिना आइटम इमेज के रूप में रेंडर होते हैं (पुराने कोड के साथ संगत)
कस्टमाइज़ेशन
कस्टम कंट्रोल
डिफ़ॉल्ट बंद करने का बटन, काउंटर और फ़ुलस्क्रीन टॉगल बदलने के लिए renderControls इस्तेमाल करें। export किए गए sub-components से जोड़ें:
import {
LightboxOverlay,
CloseButton,
Counter,
FullscreenButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderControls={({ onClose, activeIndex, count, isFullscreen, onToggleFullscreen }) => (
<div style={{ position: 'absolute', top: 12, left: 16, right: 16, display: 'flex', justifyContent: 'space-between', zIndex: 10 }}>
<Counter currentIndex={activeIndex} count={count} />
<div>
<FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />
<CloseButton onClick={onClose} />
</div>
</div>
)}
/>कस्टम जानकारी overlay
डिफ़ॉल्ट शीर्षक/विवरण gradient बदलने के लिए renderInfo इस्तेमाल करें, या उसे पूरी तरह छिपाने के लिए renderInfo={() => null} पास करें:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderInfo={({ item, index }) => (
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 16, background: 'linear-gradient(transparent, rgba(0,0,0,0.8))', color: '#fff', zIndex: 10 }}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
)}
/>कस्टम नेविगेशन
डिफ़ॉल्ट prev/next तीरों को बदलने के लिए renderNavigation इस्तेमाल करें:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderNavigation={({ onPrev, onNext, activeIndex, count }) => (
<div style={{ position: 'absolute', bottom: 24, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 12, zIndex: 10 }}>
<button onClick={onPrev} disabled={activeIndex === 0}>Prev</button>
<span>{activeIndex + 1} / {count}</span>
<button onClick={onNext} disabled={activeIndex === count - 1}>Next</button>
</div>
)}
/>कस्टम स्लाइड
पूरी तरह कस्टम स्लाइड कंटेंट के लिए renderSlide इस्तेमाल करें। डिफ़ॉल्ट इमेज स्लाइड पर लौटने के लिए null लौटाएँ:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderSlide={({ item, index, size, isActive, onReady, onError }) => {
// Custom CTA on last slide
if (index === images.length - 1) {
return (
<div style={{ width: size[0], height: size[1], display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}>
<h2>View all photos</h2>
</div>
);
}
return null; // default image slide
}}
/>कंटेंट लोडिंग और एरर हैंडलिंग
Lightbox हर स्लाइड की लोडिंग और एरर स्टेट ट्रैक करता है। कंटेंट लोड होते समय spinner दिखता है; फ़ेल हुए मीडिया पर टूटी इमेज का आइकन दिखता है। एरर वाले URL cache हो जाते हैं, इसलिए दोबारा आने पर बिना फिर से कोशिश किए तुरंत एरर दिखता है।
Lifecycle callbacks
renderSlide इस्तेमाल करते समय लोडिंग इंडिकेटर नियंत्रित करने के लिए ये callbacks कॉल करें:
| Callback | कब कॉल करें |
|---|---|
onReady | इमेज लोड हुई या वीडियो चलने लगा। लोडिंग और एरर स्टेट साफ़ करता है। |
onWaiting | वीडियो चलते-चलते buffer हो रहा है। लोडिंग इंडिकेटर दिखाता है। |
onError | कंटेंट लोड नहीं हुआ। एरर overlay दिखाता है और URL को टूटा हुआ cache करता है। |
// Inside renderSlide — wire callbacks to your custom media
renderSlide={({ item, index, size, isActive, onReady, onWaiting, onError }) => (
<div style={{ width: size[0], height: size[1] }}>
{item.type === 'video' ? (
<video
src={item.src}
poster={item.poster}
autoPlay={isActive}
onCanPlay={onReady}
onWaiting={onWaiting}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
/>
) : (
<img
src={item.src}
onLoad={onReady}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
/>
)}
</div>
)}कस्टम लोडिंग और एरर UI
डिफ़ॉल्ट spinner और एरर आइकन की जगह अपने कंपोनेंट लगाएँ:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={() => setIsOpen(false)}
renderLoading={({ item, activeIndex }) => (
<div style={{
position: 'absolute', inset: 0, zIndex: 10,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#fff', fontSize: 14,
}}>
Loading image {activeIndex + 1}...
</div>
)}
renderError={({ item, activeIndex }) => (
<div style={{
position: 'absolute', inset: 0, zIndex: 10,
display: 'flex', flexDirection: 'column',
alignItems: 'center', justifyContent: 'center',
gap: 12, color: 'rgba(255,255,255,0.5)',
}}>
<span style={{ fontSize: 48 }}>!</span>
<span>Failed to load content</span>
</div>
)}
/>URL स्टेट
लाइव डेमो देखें →LightboxUrlOverlay एक अलग कंपोनेंट है जिसकी खुले होने की स्टेट address bar में रहती है। @reelkit/react के useOverlayUrlState से कंट्रोलर बनाएँ और उसे controller के रूप में दें: जब parameter किसी स्लाइड का नाम लेता है तो गैलरी खुद खुलती है, और उसके हटते ही बंद हो जाती है। लिंक शेयर किए जा सकते हैं, और back बटन पेज छोड़ने के बजाय गैलरी बंद करता है।
बिल्ट-इन keys
आप बिल्ट-इन key से स्लाइडों को पहचान सकते हैं — कंट्रोलर में urlIndexKey (स्थिति से) या urlStableIdKey (स्थिर id से) spread करें — दोनों @reelkit/react से भी export होते हैं। URL स्टेट गाइड और Core API देखें।
import { useOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/react';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';
const photo = useOverlayUrlState({
param: 'photo',
...urlIndexKey(() => images.length),
});
// Opening is a link — the href is the open action. No open flag, no handler:
// the overlay reads the URL and opens itself.
{images.map((image, i) => (
<Link key={image.src} to={`?photo=${i}`}>
<img src={image.src} />
</Link>
))}
<LightboxUrlOverlay controller={photo} images={images} />Hook विकल्पों का एक object लेता है और UrlStateController (set, index, value के साथ) लौटाता है। प्रोग्राम से नियंत्रण के लिए इसे रखें: set वह निचले स्तर का write है जो overlay अंदर से इस्तेमाल करता है (स्लाइड बदलना, और बंद करने के लिए set(null))। यह overlay को प्रोग्राम से भी चलाता है — set(index) उसे खोलता है, बिल्कुल parameter पर जाने की तरह। फिर भी खोलने के लिए लिंक को प्राथमिकता दें: href शेयर किया जा सकता है, नए tab में खुलता है, और back बटन उसे बंद करता है — सब मुफ़्त में, बिना किसी हैंडलर के।
useOverlayUrlState के सारे विकल्प (param, adapter, codec, locator): React API रेफ़रेंस देखें।
LightboxUrlOverlay खुद सिर्फ़ controller (ज़रूरी), वैकल्पिक onClose, और LightboxOverlay के सारे दिखावट और व्यवहार वाले props (images, ariaLabel, transitionFn, render props वगैरह) लेता है — लेकिन isOpen नही ं।
- खोलने पर history की एक entry लगती है। स्लाइड बदलना उसे बदल देता है, इसलिए सौ स्वाइप भी कोई entry नहीं जोड़ते — back का एक कदम हमेशा गैलरी छोड़ देता है। Back बंद करता है; फ़ोटो में पीछे नहीं जाता।
?photo=3जैसा शेयर किया गया लिंक गैलरी को उसी स्लाइड पर खोलता है। पेज के साथ आए लिंक को बंद करना आपकी साइट से बाहर जाने के बजाय parameter को उसी जगह हटाता है।- Back सिर्फ़ तब बंद करता है जब आपने ऐप के अंदर से खोला हो — लिंक ने entry जोड़ी, इसलिए back गैलरी पर लौटता है। नए tab में सीधे खोले गए शेयर किए गए लिंक के पीछे कोई history नहीं होती, इसलिए ब्राउज़र का back साइट छोड़ देता है; बंद करने का बटन या Escape parameter को उसी जगह हटाकर आपको गैलरी पर रखता है। पैकेज के साथ आने वाले router adapters उसी पेज पर लिंक के push की पुष्टि करते हैं; जो कस्टम adapter नहीं बता सकता कि entry कैसे आई, उसे भी उसी जगह बंद होना मिलता है, जिससे history में पेज की एक नकल रह जाती है — तब back का एक कदम ऐसा लगता है जैसे कुछ हुआ ही नहीं, लेकिन कुछ दोबारा नहीं खुलता।
- किसी स्लाइड का नाम न लेने वाला parameter — पुराना bookmark, हाथ से बदली value — URL से हटा दिया जाता है, ताकि address bar ऐसी स्लाइड का दावा न करता रहे जो खुल ही नहीं सकती।
Routing वाले ऐप में adapter पास करें। history में सीधे लिखने से router की अपनी location पुरानी पड़ जाती है, और उसका अगला नेविगेशन parameter गिरा देता है।
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
const adapter = useReactRouterUrlAdapter();
const photo = useOverlayUrlState({
param: 'photo',
adapter,
...urlIndexKey(() => images.length),
});
<LightboxUrlOverlay controller={photo} images={images} />खोलना एक लिंक है। खुली स्टेट URL में रहती है, इसलिए थंबनेल एक साधारण लिंक है — कोई click हैंडलर नहीं — और ब्राउज़र का अपना व्यवहार मुफ़्त मिलता है: नए tab में खोलना, पता कॉपी करना, hover पर प्रीव्यू। Routing वाले ऐप में router का लिंक इस्तेमाल करें, ताकि सब क्लाइंट साइड पर ही रहे।
import { Link } from 'react-router-dom';
// The href is the open action — no onClick, no open flag.
{images.map((image, i) => (
<Link key={image.src} to={`?photo=${i}`}>
<img src={image.src} />
</Link>
))}
<LightboxUrlOverlay controller={photo} images={images} />शेयर होने वाले लिंक के लिए स्थिर पहचान चुनें। Index स्थिति पर आधारित है, इसलिए सूची का क्रम बदलते ही bookmark किया गया ?photo=3 कोई दूसरी इमेज खोलता है । urlStableIdKey हर आइटम की स्थिर id से key बनाता है और मौजूदा सूची में ढूँढता है — आम मामले के लिए एक कॉल काफ़ी है।
const photo = useOverlayUrlState({
param: 'photo',
...urlStableIdKey({ items: () => images }),
});
<LightboxUrlOverlay controller={photo} images={images} />URL में id को base64url में encode करने के लिए hashCodec: base64UrlCodec पास करें — वापस पढ़ा जा सकने वाला छिपाव, cryptographic hash नहीं।
किसी दूसरे field (slug) से key बनाएँ, या locateAsync से अनंत फ़ीड के पेज लोड करें, और codec/locator खुद बनाएँ:
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => images.findIndex((x) => x.slug === id),
identify: (index) => images[index].slug,
},
});
<LightboxUrlOverlay controller={photo} images={images} />अनंत या पेज वाली गैलरी। synchronous locate सिर्फ़ लोड हो चुकी इमेज के लिए जवाब दे सकता है — जिस फ़ीड में 20 इमेज लोड हुई हैं, उसकी इमेज 400 का शेयर किया गया लिंक खाली लौटता है। locateAsync विकल्प है, जो सिर्फ़ locate के न मिलने पर कॉल होता है।
छोटा रास्ता
आइ टम की id से key बना रहे हैं? अपना codec और locator लिखना छोड़ें — locateAsync सीधे urlStableIdKey({ items, locateAsync }) को पास करें (न मिलने पर यह fetch करता है, फिर index लौटाता है)। नीचे का पूरा रूप किसी दूसरे field से key बनाने या पूरे नियंत्रण के लिए है।
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => images.findIndex((x) => x.id === id),
identify: (index) => images[index].id,
locateAsync: async (id) => {
const loaded = await loadById(id); // or loadUntil(id) — fetch just that one, or page up to it
if (!loaded) return null; // exhausted — link names no image
setImages(loaded); // commit — the overlay renders from this state
return loaded.findIndex((x) => x.id === id); // wherever it landed
},
},
});
<LightboxUrlOverlay controller={photo} images={images} />- लोड कैसे करना है, यह आप पर है — लक्ष्य तक लगातार पेज fetch करें, या सिर्फ़ वही एक इमेज fetch करके जोड़ दें। URL स्थिति से नहीं, पहचान से key बनाता है, इसलिए आइटम जहाँ भी पहुँचे,
findIndexवही लौटाता है। locateAsyncके pending रहने तक lightbox बंद रहता है और parameter को छुआ नहीं जाता, इसलिए सीधा लिंक fetch के बाद भी बना रहता है।nullया rejection parameter हटा देता है।- URL आगे बढ़ जाने, बंद होने या unmount के बाद आया जवाब फेंक दिया जाता है — धीमा fetch ऐसी स्लाइड नहीं खोल सकता जो किसी ने माँगी ही नहीं।
- pending रहने के दौरान कुछ भी रेंडर नहीं होता; लोडिंग की वह स्टेट पेज के पास पहले से है, इसलिए अपना skeleton रेंडर करें।
- कोई timeout नहीं है — lightbox नहीं जान सकता कि गैलरी कितनी लंबी है। पेज ख़त्म होने पर
nullसे निपटाएँ, नहीं तो overlay हमेशा बंद रहेगा। locateAsyncजो भी लौटाए, वही अंतिम है — अभी fetch किए डेटा का index,imagesदोबारा पढ़े बिना जैसा है वैसा लिया जाता है।
अपने-आप आगे बढ़ना
खुलते ही गैलरी को स्लाइडशो की तरह चलाने के लिए autoAdvance सेट करें। यह डिफ़ॉल्ट रूप से बंद है, इसलिए मौजूदा lightbox पहले की तरह ही रेंडर होता और चलता है।
import { LightboxOverlay } from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
autoAdvance
autoAdvanceInterval={4000}
/>;- इमेज
autoAdvanceInterval(डिफ़ॉल्ट 5000 ms) तक रहती हैं, जो इमेज लोड होने के पल से गिना जाता है, इसलिए धीमी इमेज किसी के देखने से पहले कभी नहीं छूटती। जो इमेज लोड नहीं हो पाती, उसकी गिनती उसके error से होती है। - वीडियो loop की जगह एक बार चलते हैं और ख़त्म होने पर आगे बढ़ते हैं।
useVideoSlideRendererके साथ यह आपके लिए जुड़ा हुआ है; कस्टमrenderSlideअपने props सेautoAdvanceपढ़ता है और अंत की सूचनाonEndedसे देता है। - यह रुका रहता है जब माउस किसी कंट्रोल (बटन, लिंक या कैप्शन) पर टिका हो, swipe के दौरान, और जब पेज छिपा हो, फिर वहीं से आगे चलता है जहाँ रुका था।
- हाथ से आगे बढ़ना (तीर, keys, wheel, swipe) नई स्लाइड का interval शून्य से शुरू करता है; स्लाइडशो चालू रहता है।
- आख़िरी स्लाइड पर यह रुक जाता है, जब तक
loopसेट न हो। कंट्रोल में play/pause बटन स्टेट दिखाता है और दर्शक को इसे बदलने देता है।
कस्टम renderControls को isAutoAdvancing मिलता है और, सिर्फ़ autoAdvance चालू होने पर, onToggleAutoAdvance — जब यह मौजूद हो तो PlayPauseButton रेंडर करें। टाइमिंग ख़ुद @reelkit/core का createSlideshowController है; अपने प्लेयर में यही व्यवहार बनाने के लिए ऑटोप्ले गाइड देखें, और इसके मेथड के लिए Core API।
डाउनलोड
कंट्रोल में डाउनलोड बटन जोड़ने के लिए showDownload सेट करें। यह सक्रिय आइटम को उसके downloadUrl से सेव करता है, जो न हो तो src से, उसके downloadName नाम से, जो न हो तो URL के आख़िरी हिस्से के नाम से।
import { LightboxOverlay, type LightboxItem } from '@reelkit/react-lightbox';
const images: LightboxItem[] = [
{
src: 'https://cdn.example.com/beach-1600.jpg',
// Save the original rather than the resized preview
downloadUrl: 'https://cdn.example.com/beach-original.jpg',
downloadName: 'beach.jpg',
},
];
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
showDownload
/>;फ़ाइल fetch करके सेव की जाती है, क्योंकि ब्राउज़र cross-origin लिंक पर download attribute को नज़रअंदाज़ करता है और फ़ाइल को खोल देता। जब fetch विफल हो — आम तौर पर दूसरे origin का ऐसा सर्वर जो CORS headers नहीं भेजता — तो फ़ाइल नए टैब में खुलती है, जहाँ दर्शक उसे फिर भी सेव कर सकता है। एक क्लिक में सेव के लिए फ़ाइलें Access-Control-Allow-Origin के साथ serve करें। डाउनलोड चलते समय बटन disabled रहता है।
कस्टम renderControls को isDownloading मिलता है और, सिर्फ़ showDownload चालू होने पर, onDownload — जब यह मौजूद हो तो DownloadButton रेंडर करें। helper @reelkit/core का downloadFile है; डाउनलोड गाइड देखें।
API रेफ़रेंस
LightboxOverlay के props
LightboxOverlayProps
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
isOpen | boolean | ज़रूरी | lightbox का दिखना नियंत्रित करता है। URL से चलने वाली खुली स्टेट के लिए इसकी जगह अलग LightboxUrlOverlay इस्तेमाल करें — नीचे URL स्टेट देखें। |
images | LightboxItem[] | ज़रूरी | दिखाई जाने वाली इमेज का array |
ariaLabel | string | 'Image gallery' | dialog क्षेत्र का सुलभ लेबल; lightbox खुलने पर स्क्रीन रीडर इसे बोलते हैं |
initialIndex | number | 0 | शुरुआती इमेज index |
transitionFn | TransitionTransformFn | slideTransition | स्लाइड ट्रांज़िशन फ़ंक्शन। कोई बिल्ट-इन (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) import करें या अपना पास करें। छोड़ने पर slideTransition लगता है। |
autoAdvance | boolean | false | गैलरी को स्लाइडशो की तरह चलाएँ। अपने-आप आगे बढ़ना देखें। |
autoAdvanceInterval | number | 5000 | स्लाइडशो के दौरान हर इमेज कितनी देर रहती है, मिलीसेकंड में, लोड होने के बाद से गिना जाता है |
showDownload | boolean | false | सक्रिय आइटम के लिए डाउनलोड बटन जोड़ें। डाउनलोड देखें। |
apiRef | MutableRefObject<ReelApi> | - | Reel API तक पहुँचने के लिए ref |
renderControls | (props: ControlsRenderProps) => ReactNode | - | कस्टम कंट्रोल, डिफ़ॉल्ट बंद करने के बटन, काउंटर और फ़ुलस्क्रीन टॉगल की जगह लेते हैं |
renderNavigation | (props: NavigationRenderProps) => ReactNode | - | कस्टम नेव िगेशन, डिफ़ॉल्ट prev/next तीरों की जगह लेता है |
renderInfo | (props: InfoRenderProps) => ReactNode | - | कस्टम जानकारी overlay, डिफ़ॉल्ट शीर्षक + विवरण gradient की जगह लेता है। छिपाने के लिए null लौटाएँ। |
renderSlide | (props: SlideRenderProps) => ReactNode | null | - | कस्टम स्लाइड रेंडरिंग। { item, index, size, isActive, autoAdvance, onReady, onWaiting, onError, onEnded } मिलता है। डिफ़ॉल्ट पर लौटने के लिए null लौटाएँ। |
renderLoading | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | कस्टम लोडिंग इंडिकेटर, डिफ़ॉल्ट spinner की जगह लेता है |
renderError | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | कस्टम एरर इंडिकेटर, डिफ़ॉल्ट एरर आइकन की जगह लेता है |
LightboxUrlOverlay के props
LightboxUrlOverlayProps
ऊपर के सारे दिखावट और व्यवहार वाले props लेता है, सिवाय isOpen के, जिसकी जगह controller आता है। यहाँ initialIndex अनदेखा होता है — स्लाइड कंट्रोलर की स्थिति चुनती है, इसलिए साथ में दी गई value हर बार खुलने पर बदल दी जाती।
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
controller | UrlStateController | ज़रूरी | useOverlayUrlState से मिला कंट्रोलर। इसकी स्थिति तय करती है कि overlay खुला है या नहीं और कौन-सी स्लाइड दिखती है; स्लाइड बदलने और बंद होने पर overlay इसी के ज़रिए वापस लिखता है। |
Callbacks
| Prop | टाइप | विवरण |
|---|---|---|
onClose | () => void | lightbox बंद होने पर कॉल होता है। LightboxOverlay पर ज़रूरी (खुली स्टेट आपके पास है, इसलिए बंद करना आपको संभालना होगा); LightboxUrlOverlay पर वैकल्पिक, जहाँ बंद करना URL चलाता है — इसे सिर्फ़ बंद होने के बाद कुछ करने के लिए पास करें। |
onSlideChange | (index: number) => void | स्लाइड बदलने के बाद कॉल होता है |
Reel के props (आगे भेजे गए)
ये props अंदर के Reel कंपोनेंट को आगे भेजे जाते हैं। इनका टाइप ReelProxyProps है, Reel props का वह हिस्सा जो overlay आगे भेजता है; इन्हें आगे भेजने वाले wrapper को टाइप करने के लिए इसका इस्तेमाल करें।
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
loop | boolean | false | अनंत लूप चालू करता है |
enableNavKeys | boolean | true | कीबोर्ड नेविगेशन चालू करता है |
enableWheel | boolean | true | माउस व्हील से नेविगेशन चालू करता है |
wheelDebounceMs | number | 200 | व्हील debounce की अवधि (ms) |
transitionDuration | number | 300 | ट्रांज़िशन एनिमेशन की अवधि (ms) |
swipeDistanceFactor | number | 0.12 | स्वाइप की सीमा (0-1) |
swipeToCloseDirection | 'up' | 'down' | 'up' | मोबाइल पर स्वाइप करके बंद करने वाले जेस्चर की दिशा |
टाइप
LightboxItem
interface LightboxItem {
src: string;
type?: 'image' | 'video'; // defaults to 'image'
poster?: string; // thumbnail for video items
title?: string;
description?: string;
width?: number;
height?: number;
downloadUrl?: string; // what the download button saves; defaults to src
downloadName?: string; // file name; defaults to the URL's last segment
}ControlsRenderProps
interface ControlsRenderProps {
item: LightboxItem;
activeIndex: number;
count: number;
isFullscreen: boolean;
isAutoAdvancing: boolean;
isDownloading: boolean;
onClose: () => void;
onToggleFullscreen: () => void;
onToggleAutoAdvance?: () => void; // only with autoAdvance
onDownload?: () => void; // only with showDownload
}NavigationRenderProps
interface NavigationRenderProps {
item: LightboxItem;
activeIndex: number;
count: number;
onPrev: () => void;
onNext: () => void;
}SlideRenderProps
interface SlideRenderProps {
item: LightboxItem;
index: number;
size: [number, number];
isActive: boolean;
autoAdvance: boolean; // a video should play once, not loop
onReady: () => void;
onWaiting: () => void;
onError: () => void;
onEnded: () => void; // a video played to its end
}InfoRenderProps
interface InfoRenderProps {
item: LightboxItem;
index: number;
}सब-कंपोनेंट
renderControls के ज़रिए कस्टम कंट्रोल बनाने के लिए दोबारा इस्तेमाल होने वाले sub-components।
CloseButton
डिफ़ॉल्ट X बंद करने का बटन। Props (CloseButtonProps): onClick, वैकल्पिक className और style।
import { CloseButton } from '@reelkit/react-lightbox';
<CloseButton onClick={onClose} />Counter
"1 / 3" दिखाने वाली इमेज काउंटर pill। Props (CounterProps): currentIndex (शून्य से शुरू), count, वैकल्पिक className और style।
import { Counter } from '@reelkit/react-lightbox';
<Counter currentIndex={activeIndex} count={count} />FullscreenButton
फ़ुलस्क्रीन टॉगल बटन (Maximize/Minimize आइकन)। Props (FullscreenButtonProps): isFullscreen, onToggle, वैकल्पिक className और style।
import { FullscreenButton } from '@reelkit/react-lightbox';
<FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />SoundButton
वीडियो स्लाइड के लिए mute/unmute टॉगल बटन (Volume2/VolumeX आइकन)। useVideoSlideRenderer से मिले renderControls में अपने-आप शामिल है। कस्टम कंट्रोल के अंदर अलग से इस्तेमाल के लिए useSoundState से साउंड स्टेट तक पहुँचें। Props (SoundButtonProps): isMuted, onToggle, वैकल्पिक className और style।
import { SoundButton } from '@reelkit/react-lightbox';
import { useSoundState } from '@reelkit/react';
// Inside a component wrapped in SoundProvider:
function CustomControls({ onClose }) {
const soundState = useSoundState();
return (
<div>
<SoundButton
isMuted={soundState.muted.value}
onToggle={soundState.toggle}
/>
<button onClick={onClose}>Close</button>
</div>
);
}PlayPauseButton
स्लाइडशो टॉगल (Play/Pause आइकन, चलते समय aria-pressed)। Props (PlayPauseButtonProps): isPlaying, onToggle, वैकल्पिक className और style। autoAdvance चालू होने पर डिफ़ॉल्ट कंट्रोल इसे दिखाते हैं।
import {
LightboxOverlay,
PlayPauseButton,
CloseButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
autoAdvance
renderControls={({ isAutoAdvancing, onToggleAutoAdvance, onClose }) => (
<>
{onToggleAutoAdvance && (
<PlayPauseButton
isPlaying={isAutoAdvancing}
onToggle={onToggleAutoAdvance}
/>
)}
<CloseButton onClick={onClose} />
</>
)}
{...props}
/>;DownloadButton
डाउनलोड बटन (Download आइकन), isDownloading के दौरान disabled। Props (DownloadButtonProps): isDownloading, onClick, वैकल्पिक className और style। showDownload चालू होने पर डिफ़ॉल्ट कंट्रोल इसे दिखाते हैं।
import {
LightboxOverlay,
DownloadButton,
CloseButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
showDownload
renderControls={({ isDownloading, onDownload, onClose }) => (
<>
{onDownload && (
<DownloadButton isDownloading={isDownloading} onClick={onDownload} />
)}
<CloseButton onClick={onClose} />
</>
)}
{...props}
/>;Hooks
useVideoSlideRenderer
opt-in वीडियो सपोर्ट का hook। renderSlide, renderControls और SoundProvider लौटाता है — overlay को SoundProvider में लपेटें और रेंडर फ़ंक्शन पास करें। लौटाया गया टाइप UseVideoSlideRendererResult है, जिसमें hasVideo भी है; किसी भी आइटम का type: 'video' होने पर यह true होता है।
इसका renderSlide वीडियो आइटम LightboxVideoSlide से बनाता है, या नी ऐसी वीडियो स्लाइड जो एक साझा <video> एलिमेंट से चलती है, ताकि iOS पर साउंड काम करता रहे, और हर वीडियो की स्थिति याद रखती है। इसे ख़ुद सिर्फ़ कस्टम renderSlide से रेंडर करें। इसके props (LightboxVideoSlideProps) हैं src, poster, isActive, size, slideKey (सहेजी गई स्थिति और लिए गए फ़्रेम की key), loop (डिफ़ॉल्ट true) और onPlaying, onWaiting, onError, onEnded callbacks।
import { useVideoSlideRenderer } from '@reelkit/react-lightbox';
const { renderSlide, renderControls, SoundProvider, hasVideo } =
useVideoSlideRenderer(items);
// SoundProvider — wrap LightboxOverlay in this for mute/unmute support
// renderSlide — pass to LightboxOverlay's renderSlide prop
// renderControls — pass to LightboxOverlay's renderControls prop
// (includes Counter, FullscreenButton, SoundButton, CloseButton)
// hasVideo — true if items contain at least one videouseFullscreen
सभी ब्राउज़र के सपोर्ट के साथ फ़ुलस्क्रीन स्टेट संभालने का hook।
import { useRef } from 'react';
import { useFullscreen } from '@reelkit/react';
function CustomLightbox() {
const containerRef = useRef<HTMLDivElement>(null);
const [isFullscreen, requestFullscreen, exitFullscreen, toggleFullscreen] =
useFullscreen({ ref: containerRef });
return (
<div ref={containerRef}>
<button onClick={toggleFullscreen}>
{isFullscreen.value ? 'Exit Fullscreen' : 'Enter Fullscreen'}
</button>
</div>
);
}ट्रांज़िशन
transitionFn prop के ज़रिए कोई भी TransitionTransformFn पास करें। सिर्फ़ इस्तेमाल होने वाला ट्रांज़िशन import करने से bundler बाकी को tree-shake कर देता है। छोड़ने पर slideTransition लगता है।
| फ़ंक्शन | कहाँ से | विवरण |
|---|---|---|
slideTransition | @reelkit/react-lightbox | सामान्य हॉरिज़ ॉन्टल स्लाइड (डिफ़ॉल्ट) |
lightboxFadeTransition | @reelkit/react-lightbox | इमेज के बीच crossfade |
flipTransition | @reelkit/react-lightbox | 3D कार्ड पलटने का इफ़ेक्ट |
lightboxZoomTransition | @reelkit/react-lightbox | छोटे से सामान्य आकार तक zoom in |
import {
LightboxOverlay,
lightboxFadeTransition,
} from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={isOpen}
images={images}
initialIndex={0}
onClose={handleClose}
transitionFn={lightboxFadeTransition}
/>कस्टम ट्रांज़िशन फ़ंक्शन
अपना TransitionTransformFn लिखें और transitionFn के ज़रिए पास करें। इसका signature core स्लाइडर ट्रांज़िशन जैसा ही है।
import { LightboxOverlay } from '@reelkit/react-lightbox';
import type { TransitionTransformFn } from '@reelkit/react';
const customFade: TransitionTransformFn = (offset, size) => ({
transform: `translate3d(${offset * size[0]}px, 0, 0)`,
opacity: 1 - Math.min(Math.abs(offset), 1),
});
<LightboxOverlay
isOpen={isOpen}
images={images}
transitionFn={customFade}
onClose={() => setIsOpen(false)}
/>CSS क्लास
सारे UI एलिमेंट साधारण CSS क्लास इस्तेमाल करते हैं (CSS modules नहीं), जिन्हें @reelkit/react-lightbox/styles.css के बाद लोड होने वाली stylesheet में ज़्यादा specificity वाले selectors से निशाना बनाया जा सकता है। रंग, आकार और z-index बदलने के लिए नीचे थीमिंग हिस्से में बताई CSS custom properties को प्राथमिकता दें।
| क्लास | कंपोनेंट | विवरण |
|---|---|---|
.rk-lightbox-overlay | Overlay | Root कंटेनर (फ़ुलस्क्रीन backdrop) |
.rk-lightbox-spinner | Overlay | डिफ़ॉल्ट लोडिंग spinner |
.rk-lightbox-img-error | Overlay | एरर स्टेट का कंटेनर (टूटी इमेज/वीडियो) |
.rk-lightbox-img-error-text | Overlay | एरर स्टेट का टेक्स्ट लेबल |
.rk-lightbox-swipe-hint | Overlay | मोबाइल पर स्वाइप का संकेत |
.rk-lightbox-controls-left | Controls | ऊपर-बाएँ कंट्रोल का कंटेनर |
.rk-lightbox-btn | Controls | कंट्रोल बटन (फ़ुलस्क्रीन वगैरह) |
.rk-lightbox-play-pause | Controls | स्लाइडशो play/pause बटन (.rk-lightbox-btn के साथ) |
.rk-lightbox-download | Controls | डाउनलोड बटन (.rk-lightbox-btn के साथ) |
.rk-lightbox-close | Controls | बंद करने का बटन |
.rk-lightbox-counter | Controls | इमेज काउंटर chip |
.rk-lightbox-nav | Navigation | नेविगेशन तीर (दोनों) |
.rk-lightbox-nav-prev | Navigation | पिछला तीर |
.rk-lightbox-nav-next | Navigation | अगला तीर |
.rk-lightbox-info | Info | शीर्षक/विवरण का कंटेनर |
.rk-lightbox-title | Info | इमेज का शीर्षक |
.rk-lightbox-description | Info | इमेज का विवरण |
.rk-lightbox-slide | Slide | स्लाइड कंटेनर |
.rk-lightbox-img | Slide | इमेज एलिमेंट |
.rk-lightbox-video-container | VideoSlide | वीडियो स्लाइड कंटेनर (opt-in) |
.rk-lightbox-video-element | VideoSlide | वीडियो एलिमेंट (opt-in) |
.rk-lightbox-video-poster | VideoSlide | वीडियो की poster इमेज (opt-in) |
थीमिंग
हर रंग, आकार, z-index और ट्रांज़िशन एक CSS custom property में है। कंपोनेंट का source छुए बिना थीम बदलने के लिए :root (या lightbox के किसी भी ancestor) पर एक या कई override करें।
| Token | डिफ़ॉल्ट | क्या नियंत्रित करता है |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | फ़ुलस्क्रीन backdrop का रंग |
--rk-lightbox-overlay-z | 9999 | Overlay का z-index |
--rk-lightbox-top-shade-height | 80px | ऊपर के gradient की ऊँचाई |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | ऊपर के gradient का रंग |
--rk-lightbox-edge-padding | 16px | बंद / नेविगेशन / ऊपर-बाएँ कंट्रोल की किनारे से दूरी |
--rk-lightbox-controls-gap | 12px | ऊपर-बाएँ कंट्रोल के बीच की दूरी |
--rk-lightbox-transition | 0.2s | बटन hover ट्रांज़िशन की अवधि |
--rk-lightbox-blur | 8px | बटन / chips के backdrop blur का radius |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | बंद, नेविगेशन और छोटे बटन का डिफ़ ॉल्ट background |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | बंद, नेविगेशन और छोटे बटन का hover background |
--rk-lightbox-btn-fg | #fff | बंद, नेविगेशन और छोटे बटन के आइकन का रंग |
--rk-lightbox-btn-size | 36px | छोटे बटन का आकार (फ़ुलस्क्रीन टॉगल वगैरह) |
--rk-lightbox-btn-disabled-opacity | 0.5 | disabled छोटे बटन की opacity (डाउनलोड चलते समय) |
--rk-lightbox-close-size | 40px | बंद करने के बटन का आकार |
--rk-lightbox-nav-size | 48px | prev/next तीर का आकार |
--rk-lightbox-nav-opacity | 0.7 | आराम की हालत में prev/next तीरों की opacity |
--rk-lightbox-counter-fg | #fff | काउंटर के टेक्स्ट का रंग |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | काउंटर chip का background |
--rk-lightbox-counter-size | 14px | काउंटर का font size |
--rk-lightbox-counter-padding | 6px 12px | काउंटर chip की padding |
--rk-lightbox-counter-radius | 20px | काउंटर chip का border-radius |
--rk-lightbox-spinner-size | 28px | डिफ़ॉल्ट spinner की चौड़ाई/ऊँचाई |
--rk-lightbox-spinner-track | rgba(255, 255, 255, 0.2) | spinner track का रंग |
--rk-lightbox-spinner-fg | #fff | spinner इंडिकेटर का रंग |
--rk-lightbox-spinner-duration | 0.8s | spinner के घूमने की अवधि |
--rk-lightbox-error-fg | rgba(255, 255, 255, 0.4) | एरर आइकन + टेक्स्ट का रंग |
--rk-lightbox-error-text-size | 13px | एरर संदेश का font size |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | कैप्शन के पीछे का gradient |
--rk-lightbox-info-padding | 24px | कैप्शन की अंदरूनी padding |
--rk-lightbox-title-size | 18px | शीर्षक का font size |
--rk-lightbox-description-size | 14px | विवरण का font size |
--rk-lightbox-info-fg | #fff | कैप्शन के टेक्स्ट का रंग |
--rk-lightbox-hint-fg | rgba(255, 255, 255, 0.5) | स्वाइप संकेत के टेक्स्ट का रंग |
--rk-lightbox-hint-bg | rgba(0, 0, 0, 0.3) | स्वाइप संकेत chip का background |
--rk-lightbox-hint-duration | 3s | स्वाइप संकेत के दिखने/छिपने की कुल अवधि |
--rk-lightbox-video-bg | #000 | <video> के पीछे letterbox background |
नीचे का snippet @reelkit/react-lightbox/styles.css के बाद लोड होने वाली stylesheet में डालें।
/* Brand the lightbox */
:root {
--rk-lightbox-overlay-bg: #0f172a;
--rk-lightbox-btn-bg: rgba(99, 102, 241, 0.65);
--rk-lightbox-btn-bg-hover: rgba(168, 85, 247, 0.85);
--rk-lightbox-nav-size: 56px;
--rk-lightbox-counter-bg: rgba(99, 102, 241, 0.65);
--rk-lightbox-info-bg: linear-gradient(
transparent,
rgba(99, 102, 241, 0.55) 60%,
rgba(168, 85, 247, 0.85)
);
}सुलभता
Overlay का root एक modal dialog है (role="dialog", aria-modal="true")। स्क्रीन रीडर की घोषणा बदलने के लिए ariaLabel सेट करें; डिफ़ॉल्ट "Image gallery" है। हर स्लाइड पर role="group", aria-roledescription="slide" और aria-label="Image N of M" होते हैं।
Lightbox खुलने पर फ़ोकस पकड़ता है और बंद होने पर उसे trigger पर लौटा देता है। Tab और Shift+Tab अंदर के फ़ोकस होने वाले एलिमेंट में घूमते हैं; बाहर निकला फ़ोकस (बाहर क्लिक, प्रोग्राम से फ़ोकस) वापस खींच लिया जाता है। यह @reelkit/core के captureFocusForReturn और createFocusTrap से बना है।
कीबोर् ड शॉर्टकट
| कुंजी | क्रिया |
|---|---|
ArrowLeft | पिछली इमेज |
ArrowRight | अगली इमेज |
Escape | lightbox बंद करें (या चालू हो तो फ़ुलस्क्रीन से बाहर आएँ) |