Building blocks

Building blocks

<Lightbox> and <LightboxGallery> are assembled from smaller components, and every one of them is exported. When the props on the ready made components do not reach far enough, the same parts can be arranged by hand.

Reach for this last

Composing by hand means owning the wiring that <Lightbox> normally does, including the open state and the close handling. Check whether customization covers the need first, since it passes html props straight to the element behind each part.

The shape of a lightbox

A lightbox is a cover holding a modal, which holds a header, a body and a footer. The whole stack is moved to the end of <body> so that no parent's overflow or stacking context can clip it.

A
<script>
    import {
        BodyChild,
        ModalCover,
        Modal,
        LightboxHeader,
        LightboxBody,
        LightboxFooter,
        LightboxThumbnail
    } from 'svelte-lightbox'

    let isVisible = false
</script>

<LightboxThumbnail on:click={() => { isVisible = true }}>
    <img src="/img/cat.jpg" alt="A cat">
</LightboxThumbnail>

{#if isVisible}
    <BodyChild>
        <ModalCover transitionDuration={300} on:click={() => { isVisible = false }}>
            <Modal transitionDuration={300} imagePreset="">
                <LightboxHeader
                    imagePreset=""
                    showCloseButton={true}
                    enableEscapeToClose={true}
                    on:close={() => { isVisible = false }}
                />
                <LightboxBody imagePreset="" enableImageExpand={false}>
                    <img src="/img/cat.jpg" alt="A cat">
                </LightboxBody>
                <LightboxFooter imagePreset="" title="A cat" description="Photographed last summer"/>
            </Modal>
        </ModalCover>
    </BodyChild>
{/if}
svelte

The parts

<BodyChild>

Moves its slot to the end of <body> on mount and takes it away again on destroy. Nothing else. This is what keeps a lightbox out of the layout it was written in.

<ModalCover>

The full screen backdrop, carrying the .svelte-lightbox-overlay class. Fades in over transitionDuration * 2 milliseconds and out over half of it, and forwards on:click.

proptype
transitionDurationnumberBase duration the fade is calculated from

<Modal>

The box the image sits in, carrying .svelte-lightbox-main. Forwards on:click, which is what a lightbox listens to in order to tell a click on the image apart from a click on the backdrop.

proptype
transitionDurationnumberDuration of the fade
imagePresetImagePreset'', 'fullscreen' or 'scroll'

<LightboxHeader>

The bar above the image, carrying .svelte-lightbox-header. Holds the close button and dispatches close both when that button is pressed and, while enableEscapeToClose is on, when escape is pressed.

proptype
imagePresetImagePresetPreset the header should follow
showCloseButtonbooleanRenders the close button
enableEscapeToClosebooleanDispatches close on the escape key
closeButtonPropsHTMLButtonElementHtml props for the close button

<LightboxBody>

The area holding the image, carrying .svelte-lightbox-body. It sizes whatever is placed in its slot, which is why the image itself needs no styling of its own.

proptype
imagePresetImagePresetPreset the body should follow
enableImageExpandbooleanLets the image grow past its own resolution
elementHTMLDivElementBindable reference to the body element

<LightboxFooter>

The strip under the image, carrying .svelte-lightbox-footer. Renders the title in an <h2> and the description in an <h5>. Passing gallery adds the counter underneath them.

proptype
imagePresetImagePresetPreset the footer should follow
titlestringShown in an <h2>
descriptionstringShown in an <h5>
galleryGalleryState{ imageCount, activeImage }, adds the counter

<LightboxThumbnail>

A clickable wrapper carrying .svelte-lightbox-thumbnail, forwarding on:click. This is what <Lightbox> puts around the thumbnail slot.

Gallery parts

A gallery adds navigation on top of the same stack. <GalleryController> holds the arrows, the keyboard handling and the swipe gesture, and expects the stores a gallery keeps its state in.

proptype
imageCountStoreWritable<number>How many images the gallery holds
activeImageStoreWritable<number>Index of the displayed image
arrowsConfigStoreWritable<GalleryArrowsConfig>Arrow colour, edge behaviour, keyboard
swipeConfigStoreWritable<GallerySwipeConfig>Swipe settings
bodyElementHTMLDivElementThe element drags are read from

<PreviousImageButton> and <NextImageButton> are the arrows themselves. Both forward on:click and disable themselves at the edges of the gallery unless character is 'loop'.

proptype
activeImagenumberIndex of the displayed image
imageCountnumberHow many images there are, next arrow only
characterGalleryArrowCharacter'', 'hide' or 'loop'
Stores, not values

The gallery parts take stores rather than plain values because the same state is read by several components at once. A hand composed gallery has to create those stores and keep them updated itself.

Changing the counter wording

The gallery counter comes from a store, so replacing the function replaces the wording everywhere.

src/routes/+layout.svelte
<script>
    import { i18n } from 'svelte-lightbox'

    $i18n.generateLocalizedGalleryCounter = (activeImage, imageCount) => {
        return `Obrázok ${activeImage + 1} z ${imageCount}`
    }
</script>
svelte

The function receives a zero based index, which is why the examples add one before showing it.

Last update at: 2026/08/13 12:45:07