Skip to content

dromru/react-photoswipe-gallery

Repository files navigation

react-photoswipe-gallery

codecov npm

A configurable and flexible React component wrapper around PhotoSwipe.

ℹ️ Current version of react-photoswipe-gallery is compatible with PhotoSwipe v5.
If you need PhotoSwipe v4, use react-photoswipe-gallery v1.

Basic Usage

import 'photoswipe/dist/photoswipe.css'

import { Gallery, Item } from 'react-photoswipe-gallery'

const MyGallery = () => (
  <Gallery>
    <Item
      original="https://placekitten.com/1024/768?image=1"
      thumbnail="https://placekitten.com/80/60?image=1"
      width="1024"
      height="768"
    >
      {({ ref, open }) => (
        <img ref={ref} onClick={open} src="https://placekitten.com/80/60?image=1" />
      )}
    </Item>
    <Item
      original="https://placekitten.com/1024/768?image=2"
      thumbnail="https://placekitten.com/80/60?image=2"
      width="1024"
      height="768"
    >
      {({ ref, open }) => (
        <img ref={ref} onClick={open} src="https://placekitten.com/80/60?image=2" />
      )}
    </Item>
  </Gallery>
)

Demo

Check out the Storybook to see it in action πŸš€

Stories are written as real-world examples, so you can see them at the bottom of Storybook UI in the Story tab. Or browse the source code on GitHub. It covers most of the use-cases and provides examples for configuration options.

Installation

yarn add photoswipe react-photoswipe-gallery

or

npm install photoswipe react-photoswipe-gallery --save

Hash Navigation

You should pass a unique id prop to <Gallery /> component, to enable hash navigation.

Optionally, you can also pass the id to <Item /> component. Otherwise, the index will be used.

const MyGallery = () => (
  <Gallery id="my-gallery">
    <Item
      id="first-pic"
      {/*...*/}
    />
    <Item
      id="second-pic"
      {/*...*/}
    />
  </Gallery>
)

Example

Captions

If you want to add captions to your slides, you need to pass withCaption prop to the <Gallery /> and pass caption prop to each <Item />. It accepts html as well. If caption isn't provided - it will use alt prop.

const MyGallery = () => (
  <Gallery withCaption>
    <Item
      caption="Foo"
      {/*...*/}
    />
    <Item
      alt="Bar"
      {/*...*/}
    />
  </Gallery>
)

Example

Plugins

You can use native PhotoSwipe plugins with plugins prop. It accepts the function in which you should register all of your plugins, providing pswpLightbox to the plugin constructor.

Example for photoswipe-dynamic-caption-plugin:

import 'photoswipe-dynamic-caption-plugin/photoswipe-dynamic-caption-plugin.css'
import PhotoSwipeDynamicCaption from 'photoswipe-dynamic-caption-plugin'

const MyGallery = () => (
  <Gallery
      plugins={(pswpLightbox) => {
        // register plugin
        const captionPlugin = new PhotoSwipeDynamicCaption(pswpLightbox, {
          captionContent: (slide) => slide.data.alt,
        })

        // register another plugin
        // ...
      }}
  >
    {/*...*/}
  </Gallery>
)

Example

Custom UI Elements

You can add custom UI elements to PhotoSwipe with uiElements prop. It accepts an array of configuration objects for custom UI elements.

const uiElements = [
  // add custom UI element
  {
    name: 'custom-button',
    ariaLabel: 'Custom button',
    order: 9,
    isButton: true,
    html: {
      isCustomSVG: true,
      inner:
        '<path d="<ICON_PATH>" id="pswp__icn-cstm-btn"/>',
      outlineID: 'pswp__icn-cstm-btn',
    },
    appendTo: 'bar',
    onInit: (el, pswpInstance) => {
      // do something on UI element's init event
    },
    onClick: (e, el, pswpInstance) => {
      // do something on UI element's click event
    },
  },

  // add another custom UI element
  // ...
]

const MyGallery = () => (
  <Gallery uiElements={uiElements}>
    {/*...*/}
  </Gallery>
)

Example

Custom slide content

You can add your own custom slide content with content and html props.

const MyGallery = () => (
  <Gallery>
    <Item
      content={<h1>Hi!</h1>}
      {/*...*/}
    />
    <Item
      html="<h1>Hi!</h1>"
      {/*...*/}
    />
  </Gallery>
)

Example

Access to Photoswipe instance

If you need to get access to Photoswipe instance (for example, to subscribe on Photoswipe events or call some Photoswipe method), you can do it via onOpen and onBeforeOpen props of Gallery component.

onBeforeOpen triggers before PhotoSwipe.init() call.

onOpen triggers after PhotoSwipe.init() call.

onBeforeOpen and onOpen will receive PhotoSwipe instance as the first argument.

const onBeforeOpen = (pswpInstance) => {
  pswpInstance.on('change', () => {
    console.log('slide was changed')
  })
}

const onOpen = (pswpInstance) => {
  pswpInstance.currSlide.zoomTo(
    1,
    { x: 0, y: 0 },
    2000,
    false
  )
}

const MyGallery = () => (
  <Gallery onBeforeOpen={onBeforeOpen} onOpen={onOpen}>
    {/*...*/}
  </Gallery>
)

Photoswipe customization

If you need to customize Photoswipe options or Photoswipe styling you can do it via options prop of Gallery component.

const options = {
  arrowPrev: false,
  arrowNext: false,
  zoom: false,
  close: false,
  counter: false,
  bgOpacity: 0.2,
  padding: { top: 20, bottom: 40, left: 100, right: 100 },
}

const MyGallery = () => (
  <Gallery options={options}>
    {/*...*/}
  </Gallery>
)

Props

Gallery

Prop Type Required Description
id Number or String βœ“ (for hash navigation) Item ID, for hash navigation
options Object Object containing PhotoSwipe options and styling properties
plugins Function Function for registering PhotoSwipe plugins. You should pass photoswipeLightbox to each plugin constructor (example)
uiElements Array Array of configuration objects for custom UI elements. Use it for adding custom UI elements (example)
onBeforeOpen Function Triggers before PhotoSwipe.init() call. Use it for accessing PhotoSwipe API. It will receive PhotoSwipe instance as the first argument: (photoswipe: PhotoSwipe) => void
onOpen Function Triggers after PhotoSwipe.init() call. Use it for accessing PhotoSwipe API. It will receive PhotoSwipe instance as the first argument: (photoswipe: PhotoSwipe) => void
withCaption Boolean βœ“ (for default captions) Enables built-in caption display. Use the caption prop of the Item component to control caption text (example)
withDownloadButton Boolean βœ“ (for download button) Adds UI control for downloading the original image of the current slide (example)

Item

Should be children of the Gallery.

Prop Type Required Description
children Function βœ“ Render prop for exposing Gallery API
original String Url of original image
originalSrcset String Srcset of original image (example)
thumbnail String Url of thumbnail
width Number or String Width of original image
height Number or String Height of original image
alt String Alternate text for original image
caption String Text or html for caption (example)
cropped Boolean Thumbnail is cropped (example)
content ReactElement Custom slide content (example)
html String Custom slide content (raw html) (example)
id Number or String Item ID, for hash navigation (example)

Note about Item's children render prop

Item accepts only function as children.

export interface ChildrenFnProps<NodeType extends HTMLElement> {
  /**
   * Ref callback to any html node of item.
   * It must be set to HTML Element in order to work.
   * Can be done like usual ref: ref={ref}
   * or callback-way if you need extra work done with node:
   * ref={(node) => {
   *   ref(node)
   *   ...
   * }}
   */
  ref: (node: NodeType | null) => void

  /**
   * Function that opens the gallery at the current item
   */
  open: (e: MouseEvent) => void
}

<Item>
  {({ ref, open }) => (
    <img ref={ref} onClick={open} />
  )}
</Item>

<Item>
  {({ ref, open }) => (
    <span
      ref={(node) => {
        ref(node)
        console.log(node)
      }}
      onClick={open}
    >
      Open gallery
    </span>
  )}
</Item>

Hooks

useGallery

The useGallery hook returns an object with some useful methods.

Property Type Description
open (index: number) => void This function allows programmatically open Photoswipe UI at index

useGallery hook gets context provided by Gallery component. So to use useGallery hook you need to store your gallery content as separate component and then wrap it into Gallery component.

const GalleryContent = () => {
  const { open } = useGallery()

  useEffect(() => {
      open(1) // you can open second slide by calling open(1) in useEffect
  }, [open])

  return (
    <div>
      {/* or you can open second slide on button click */}
      <button onClick={() => open(1)}>Open second slide</button>
      <div>
        <Item>...</Item>
        <Item>...</Item>
        <Item>...</Item>
      </div>
    </div>
  )
}

const MyGallery = () => {
  return (
    {/* Gallery component provides context for useGallery hook used in GalleryContent */}
    <Gallery>
      <GalleryContent />
    </Gallery>
  )
}

Example

Requirements

Development

yarn install
yarn sdks vscode

then

yarn storybook

or

yarn start

License

MIT