Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

@wethegit/react-gallery

A slideshow-style gallery component for use in React projects.

Features

  • Accessibility. Built with careful attention to focus management and assistive technology.
  • Fully-customizable layouts, using CSS Custom Properties.
  • Provides a custom React hook for accessing gallery data, state, updater functions, and more. Build anything on top of the existing gallery components!

Getting started

Install the package

npm install @wethegit/react-gallery
-or-
yarn add @wethegit/react-gallery

Import the Components and—optionally—the hook.

import{Gallery,GalleryMain,GalleryItem,GalleryNav,GalleryPagination,GalleryPaginationItem,useGallery,}from"@wethegit/react-gallery"

Import the base stylesheet

This is an optional step, but it's highly recommended to use the base styles as a starting point. The most straightforward way to do this is to import the stylesheet into your app directly from the /node_modules/@wethegit/react-gallery/dist/ directory, but you can do this in whichever way is preferable within your build system or framework.

// app.jsimport"@wethegit/react-gallery/style.css"

Create a simple Gallery

The <Gallery> component is a React context provider, which gives all child components access to relevant data. All child components that need access to gallery data must live within a <Gallery>. Here's an example of a gallery, given the following contrived data GALLERY_ITEMS. This will be explained in detail shortly.

⚠️ Before continuing, make sure you have properly imported the base stylesheet, if you intend to use it.

// some-data.jsexportconstGALLERY_ITEMS=[{image: "/my-image-1.png",alt: "Description of image!",id: 131789},{image: "/my-image-2.png",alt: "Description of image!",id: 235233},{image: "/my-image-3.png",alt: "Description of image!",id: 987432},{image: "/my-image-4.png",alt: "Description of image!",id: 768324},]
// your-gallery.jsimport{GALLERY_ITEMS}from"./some-data"import{Gallery,GalleryMain,GalleryNav,GalleryPagination,GalleryPaginationItem,GalleryItem,}from"@wethegit/react-gallery"constYourGallery=()=>{return(<Galleryitems={GALLERY_ITEMS}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/><GalleryNavdirection={0}>⬅️</GalleryNav><GalleryNavdirection={1}>➡️</GalleryNav><GalleryPaginationrenderPaginationItem={({ item, index, active })=>(<GalleryPaginationItemkey={item.id}index={index}active={active}><span>{index+1}</span></GalleryPaginationItem>)}/></Gallery>)}exportdefaultYourGallery

The first step is to give your data to the <Gallery> component via the items prop. At the very least, items is expected to be an Array. From there, you're free to arrange the child components this package provides as you see fit. Below is a brief description of each of the child components' usage. For a detailed breakdown of this component, jump ahead to the Gallery section.

<GalleryMain> is the primary gallery view where your item data is rendered. It receives a render prop, renderGalleryItem, which exposes a few arguments you can use in the JSX you return: item, index, activeIndex, and active and expects a <GalleryItem> to be returned. For a detailed breakdown of this component, jump ahead to the GalleryMain section.

We're using the <GalleryNav> component to define our "next" and "previous" buttons. These components receive a direction prop, which expects either a 1 or a 0, and corresponds to the direction the gallery should move in when the button in question is clicked (where 0 maps to "previous", and 1 maps to "next"). For a detailed breakdown of this component, see the GalleryNav section.

We're also using the <GalleryPagination> and GalleryPaginationItem components here. If you're not familiar, "pagination" refers to what is often rendered as a set of "dots" below a gallery — but this can be anything (thumbnails, icons, and so on). This component receives the render prop, renderPaginationItem, which exposes a few arguments you can use in the JSX you return: item, i, activeIndex, and active. The easiest way to link up you pagination is to use the <GalleryPaginationItem> component, as shown in the example above. For a detailed breakdown of this component, jump ahead to the GalleryPagination section.

Custom layouts

Assuming the base stylesheet is being used, the <GalleryMain> component renders as a CSS grid, with a single grid area to house the currently-active item. There are a number of CSS custom properties on the various gallery components, which will allow you to customize the look of the gallery. Most of these properties are used to calculate the horizontal positioning of the gallery items around the main grid cell, and can be adjusted to create many different layout variations. Remove the transform property of a .gallery__item entirely, and you'll get a traditional, stacked gallery.

CSS custom propertyElement where definedDefaultDescription
--item-width.gallerymin(75%, 1000px)The width of the items rendered by <GalleryMain>.
--gap.gallery25vwThe horizontal gap between the gallery items.
--active-gap.gallery0pxThe horizontal gap between the currently-active gallery item and the rest.
--duration.gallery0The duration of the transition between active gallery items.
--touch-offset.gallery0The x-offset of the gallery layout, as a result of a pointermove action.
--position-offset.gallery0pxA general x-offset, if needed.
--i.gallery__itemThe index of the gallery item.
--center-offset.gallery__itemThe offset of the item from the "middle" item, in number of items.
--index-offset.gallery__itemThe offset of the item from the active item, in number of items.
--side.gallery__itemEither -1, 0, or 1; mapping to "left", "center", and "right" of the middle item.
--active.gallery__itemEither 1 or 0, mapping to true or false.
--x.gallery__itemThe final calculation of a gallery item's x position. Use this as a translateX value when customizing transform properties.
--gap.gallery__pagination10pxThe CSS grid gap between pagination items.

Components and options

<Gallery>

The parent component for all gallery instances. This serves as a React context provider to its children.

Props:

PropTypeDefault valueDescription
ariaLiveTextString"Item $i of $t."aria-live region text, which assistive technologies will announce when the active gallery item changes. More information below.
classNameString
draggableBooleantrueAllows user to advance the gallery by dragging their finger or pointing device.
itemsArrayThe gallery data.
loopBooleanfalseAllows for looping behavior — i.e. when the last gallery item is active, a user can navigate to the "next" slide, which will take them back to the start of the gallery.
onChangeFunctionFunction to run on gallery state update, as a result of a "next", "previous", or "pagination" action. More information below.
startIndexNumber0The index of the gallery item that should be active to start.
visibleRangeNumber-1The number of gallery items "visible" to the left or right of the active one. This option adds data-item-visible="true" all elements within this range, allowing for layout customization. The default value of -1 specifies that all items are visible, while setting the value to 1 for example, would add the data-item-visible="true" to those items within immediate proximity to the active item and the ones that are not in the proximity would have data-item-visible="false"

ariaLiveText

Per the WAI-ARIA spec, browsers using assistive technology such as screen readers announce content within a DOM element containing the aria-live attribute, anytime it changes. The gallery component's default value for this prop is "Item $i of $t.". The variables $i and $t here correspond to "current index" and "total number of items", and will be replaced with those values when rendered. This level of customization is provided so that you can localize/translate your content with accuracy.

onChange

The onChange callback allows you to run code whenever the active gallery item changes. It receives a single Object as an argument, containing the following properties:

ArgumentTypeDescription
oldIndexNumberThe index before the onChange function fired.
newIndexNumberThe index after the onChange function fires.
directionNumberEither 0 or 1, mapping to previous/left or next/right.

<GalleryMain>

The primary gallery body. Must be used within a <Gallery>. Renders an unordered list (<ul>) of your gallery items.

Props:

PropTypeDescription
classNameString
renderGalleryItemFunctionA render prop, returning the JSX to render for each gallery item. More information below.

renderGalleryItem

This render prop expects a <GalleryItem> to be returned, and receives a handful of arguments:

ArgumentTypeDescription
activeBooleanWhether the current item being iterated over is the active item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current item being iterated over.
itemAnyThe current item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

<GalleryItem>

Required component that wraps each child inside the renderGalleryitem prop. Renders a list item (<li>) and can accept the following props:

PropTypeDescription
childrenJSX
classNameString
activeBooleanWhether the current item being iterated over is the active item.
indexNumberThe index of the currently active gallery item.

<GalleryNav>

The navigational "next" and "previous" buttons. Must be used within a <Gallery>. You can render your buttons either by passing regular JSX children to them, or by using the renderNavItem render prop.

Props:

PropTypeDescription
childrenJSXOptional. Pass children to the component to render them as children of the implicit <button> element.
classNameString
directionNumber
renderNavItemFunctionOptional. For more control over rendering than the children prop would give you, you have the option to pass JSX to this render prop. More information below.

renderNavItem

This render prop is a nice alternative to simply passing childern to the <GalleryNav>, as it provides you with a handful of arguments, as outlined below:

ArgumentTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
disabledBooleanWhether the nav button is in its disabled state — presumably from <Gallery> having its loop prop set to false. Note that the DOM node attributes disabled and aria-disabled="true" are already added to the rendered button; whereas the purpose of this argument is to allow you to render something different based on that disabled state, or write some other rendering code based on it.

<GalleryPagination>

Renders an unordered list (<ul>) of pagination items. Must be used within a <Gallery>.

Props:

PropTypeDescription
classNameString
renderPaginationItemFunctionA render prop, returning the JSX to render for each pagination item. More information below.

renderPaginationItem

This render prop receives a handful of arguments, and is necessary for rendering pagination UI:

ArgumentTypeDescription
activeBooleanWhether the current pagination item being iterated over corresponds to the active gallery item.
activeIndexNumberThe index of the currently active gallery item.
indexNumberThe index of the current pagination item being iterated over.
itemAnyThe current pagination item being iterated over, as defined by the Array fed to the <Gallery> component's items prop.

Example usage of renderPaginationItem render prop

<GalleryPaginationrenderPaginationItem={({ index, active, activeIndex, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}><span>{index+1}</span></GalleryPaginationItem>)}/>

<GalleryPaginationItem>

Used in the prop renderPaginationItem of <GalleryPagination>. This component with a return value in a list item (<li>) and a <button>, and receives a handful of arguments:

Props:

PropTypeDescription
activeBooleanRequired. Boolean to set the <button>'s aria-current attribute.
buttonClassNameStringSet the <button> element's class.
buttonPropsObjectPass props to the <button> element.
childrenJSXPass children to the component to render them as children of the implicit <button> element.
classNameStringSet the <li> element's class.
indexNumberRequired. This needs to be a unique identifier for the <li> element, corresponding to the index of the Gallery Item being iterated over. It is used to set the gallery's active item to the associated pagination item button clicked.
decorativeOnlyBooleanA boolean to disable the interactive functionality and the onClick callback
itemTagReact.ElementTypeElement type to replace the default <button> element, so that the pagination can be used for decorative purposes
onClickFunctionThis is a curried callback function to hook into the onClick handler on the <button> element. The curried callback returns an object containing {event,index}. event is a MouseClickEvent and index is the index of the PaginationItem. Note that this is specific to the pagination buttons; if you want a piece of code to run when the active item changes regarless of what triggered that change, opt for the onChange callback instead (passed to the <Gallery> component.)

Example usage of GalleryPaginationItem

consthandlePaginationItemClick=({ event, index })=>{console.log(event,index)}<GalleryPaginationrenderPaginationItem={({ index, active, item })=>(<GalleryPaginationItemindex={index}active={active}key={item.id}onClick={handlePaginationItemClick}><span>{index+1}</span></GalleryPaginationItem>)}/>

Accessibility

The gallery component handles tabbing, focus management, and live-region announcements out-of-the-box. All relevant patterns used in this component follow the guidelines for carousels as documented by the Web Accessibility Initiative.

That said, you must still code responsibly! Ensure that your gallery contents, nav buttons, and pagination items all have discernible text — whether that be image alt text, or visually-hidden text intended for screen readers (the stylesheet included with the Gallery component inclues a CSS utility class for this: .gallery-util-visually-hidden).

Regarding the ARIA-live text, check out the section on this gallery's ariaLiveText prop.

Reduced motion

For reduced motion implementations, you can detect the user's preference via the matchMedia API, and adjust the CSS custom property, --duration on the .gallery selector. Check out the example below, which adds a style tag to the gallery, and overrides the --duration property based on the preference. You could also do this via a conditional className, if you prefer. An alternative is be to pass this prefersReducedMotion value as a prop, which could be helpful if you use the Styled Components library.

constYourGallery=()=>{// Get the user's motion preference:constprefersReducedMotion=window.matchMedia("(prefers-reduced-motion: reduce)").matches// Create an inline style objectconststyle={"--duration": prefersReducedMotion ? "0s" : "0.5s",}return(// Pass the style overrides to the <Gallery> component<Galleryitems={GALLERY_ITEMS}style={style}><GalleryMainrenderGalleryItem={({ item, index, active })=>(<GalleryItemkey={item.id}index={index}active={active}><imgsrc={item.image}alt={item.alt}/></GalleryItem>)}/>{/* ...etc */}</Gallery>)}

useGallery hook

The gallery package exposes a useGallery React hook. It returns a single object, the properties of which are outlined below.
⚠️useGallerymust be called from within a <Gallery> context.

PropertyTypeDescription
activeIndexNumberThe index of the currently-active gallery item.
draggableBooleanThe value of the <Gallery>'sdraggable prop.
galleryItemsArrayThe array of items as passed to the <Gallery>'s items prop.
goToIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Subsequently fires the onChange function, if one exists.
itemNodesReact RefA React Ref, the current property of which is bound to an Array of the actual Gallery item DOM nodes.
loopBooleanThe value of the <Gallery>'sloop prop.
nextFunctionIncrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
onChangeFunctionThe value of the <Gallery>'sonChange prop. For more information, refer to the onChange section.
previousFunctionDecrements the gallery's active index, if possible. Subsequently fires the onChange function, if one exists.
previouslyActiveIndexNumberThe index of the previously-active gallery item.
setActiveIndexFunctionReceives a single argument (Number), and sets the gallery's active item to the item at that index. Does not fire the onChange function. It is best to use goToIndex instead.
setPreviouslyActiveIndexFunctionReceives a single argument (Number), and sets the gallery's previously active item index.
setTouchStateFunctionState setter function, as defined by React's useState hook. Expects a touchState object — for more information, print the touchState variable returned from this hook in your console.
startIndexNumberThe value of the <Gallery>'sstartIndex prop.
swipeThresholdNumberAn integer specifying the minimum number of pixels a user has to drag their pointer before a gallery item change is registered.
touchStateObjectData pertaining to a pointer action, if the <Gallery>'sdraggable prop is set to true. Print this variable to your console for more information.
visibleRangeNumberThe value of the <Gallery>'svisibleRange prop.

Advanced customization

As you can see above, many of the properties returned by the useGallery hook provide the lower-level functionality for the gallery itself. You can use this data to create any custom implementation you like, without having to worry too much about what's happening behind the scenes.

Usage

Here's an example of a custom component that uses the useGallery hook to simply render a paragraph with some information about the gallery's current state:

import{useGallery}from"@wethegit/react-gallery"constGalleryInfo=()=>{const{ activeIndex, previouslyActiveIndex, loop }=useGallery()return(<p>
The gallery's current index is {activeIndex}, its last index was {previouslyActiveIndex},
and its <code>loop</code> prop is set to {String(loop)}!
</p>)}exportdefaultGalleryInfo

About

A slideshow-style gallery component for use in React projects.

Resources

Stars

4 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages