πŸ”Ž Medium.com style image zoom for React πŸ”

Last update: Jul 31, 2022

react-medium-image-zoom

All Contributors npm version npm downloads bundlephobia size Coverage Status

This library is a React.js implementation of Medium.com's image zoom that allows for images to work together for a β€œzooming” effect and works regardless of parent elements that have overflow: hidden or parents with transform properties.

As an added bonus, it will let you zoom anything (see the Storybook Examples for more).

Blog post announcing v4

Links

Installation

npm i react-medium-image-zoom

or

yarn add react-medium-image-zoom

or

">

<script src="https://unpkg.com/react-medium-image-zoom">script>

Basic Usage

Uncontrolled component (default)

Import the component and the CSS, wrap whatever you want to be "zoomable" with this component, and the component will handle it's own state:

) export default MyComponent ">
import React from 'react'
import Zoom from 'react-medium-image-zoom'
import 'react-medium-image-zoom/dist/styles.css'

const MyComponent = () => (
  <Zoom>
    <img
      alt="that wanaka tree"
      src="/path/to/thatwanakatree.jpg"
      width="500"
    />
  </Zoom>
)

export default MyComponent

You can zoom anything, so ,

, and even
elements are all valid:

that wanaka tree //
that wanaka tree
That Wanaka Tree
//
that looks like a circle
">
// 
      
<Zoom>
  <picture>
    <source media="(max-width: 800px)" srcSet="/path/to/teAraiPoint.jpg" />
    <img
      alt="that wanaka tree"
      src="/path/to/thatwanakatree.jpg"
      width="500"
    />
  </picture>
</Zoom>

// 
      
<figure> <Zoom> <img alt="that wanaka tree" src="/path/to/thatwanakatree.jpg" width="500" /> </Zoom> <figcaption>That Wanaka Tree</figcaption> </figure> //
that looks like a circle <Zoom> <div aria-label="A blue circle" style={{ width: 300, height: 300, borderRadius: '50%', backgroundColor: '#0099ff' }} /> </Zoom>

Controlled component (Controlled)

Import the Controlled component and the CSS, wrap whatever you want to be "zoomable" with this component and then dictate the zoomed/unzoomed state to the component. Here, we will automatically zoom the component once the image has loaded:

) ) export default MyComponent ">
import React, { useCallback, useState } from 'react'
import { Controlled as ControlledZoom } from 'react-medium-image-zoom'
import 'react-medium-image-zoom/dist/styles.css'

const MyComponent = () => {
  const [isZoomed, setIsZoomed] = useState(false)

  const handleImgLoad = useCallback(() => {
    setIsZoomed(true)
  }, [])

  const handleZoomChange = useCallback(shouldZoom => {
    setIsZoomed(shouldZoom)
  }, [])

  return (
    <ControlledZoom isZoomed={isZoomed} onZoomChange={handleZoomChange}>
      <img
        alt="that wanaka tree"
        onLoad={handleImgLoad}
        src="/path/to/thatwanakatree.jpg"
        width="500"
      />
    </ControlledZoom>
  )
)

export default MyComponent

The onZoomChange prop accepts a callback that will receive true or false based on events that occur (like click or scroll events) to assist you in determining when to zoom and unzoom the component.

There is also an example in the Storybook Examples of how to use a Controlled component to create a full-screen slideshow gallery.

API

Both uncontrolled & controlled components

You can pass these options to either the default or controlled components.

Prop Type Required Default Details
closeText String no 'Unzoom Image' Accessible label text for when you want to unzoom
openText String no 'Zoom Image' Accessible label text for when you want to zoom
overlayBgColorEnd String no 'rgba(255, 255, 255, 0.95)' Modal overlay background color at end of zoom
overlayBgColorStart String no 'rgba(255, 255, 255, 0)' Modal overlay background color at start of zoom
portalEl Element no document.body DOM Element to which we will append the zoom modal
scrollableEl Window no window DOM Element to which we will listen for scroll events to determine if we should unzoom
transitionDuration Number no 300 Transition duration in milliseconds for the component to use on zoom and unzoom. Set this to 0 to disable the animation
wrapElement String no 'div' Wrapper element
wrapStyle Object no null Optional style object to pass to the wrapper element. Useful when you want the container to be width: '100%', for example
zoomMargin Number no 0 Offset in pixels the zoomed image should be from the window' boundaries
zoomZindex Number no 2147483647 z-index value for the zoom overlay

Only the controlled component

You can pass these options to only the controlled component.

Prop Type Required Default Details
isZoomed bool yes false Tell the component whether or not it should be zoomed
onZoomChange Function no Function.prototype Listen for hints from the component about when you should zoom (true value) or unzoom (false value)

Migrating From v3 to v4

In v3, you might have code like this:

<ImageZoom
  image={{
    src: '/path/to/bridge.jpg',
    alt: 'Golden Gate Bridge',
    className: 'img',
    style: { width: '50em' }
  }}
  zoomImage={{
    src: '/path/to/bridge-big.jpg',
    alt: 'Golden Gate Bridge'
  }}
  zoomMargin={80}
/>

In v3, you would pass properties for your normal image that would be zoomed, and you would pass an optional zoomImage that would be a higher quality image that would replace the original image when zoomed.

The problem with v3 was that it tried to assume too many things about what it is you were trying to zoom, and this resulted in overly complex and near-unmaintainable code that had a number of bugs.

In v4, you can zoom the bridge example above like this:

">
<Zoom zoomMargin={40}>
  <img
    src="/path/to/bridge.jpg"
    alt="Golden Gate Bridge"
    className="img"
    style={{ width: '50em'}}
  />
</Zoom>

We've removed the zoomImage functionality (there is an issue for us to consider re-adding something like it), but as it was not a primary use case for many consumers, we opted to ship v4 without it.

Please see the Controlled component (Controlled) section for further documentation regarding controlled components that used the isZoomed, onZoom, and onUnzoom properties.

Contributors

Thanks goes to these wonderful people (emoji key):


Cameron Bothner

πŸ’» πŸ“– πŸ› πŸ’‘ πŸ€” πŸ‘€ ⚠️

Jeremy Bini

πŸ’» πŸ›

ismay

πŸ› πŸ€”

Rajit Singh

πŸ›

Roberto Saccon

πŸ›

wtfdaemon

πŸ›

Robert Pearce

πŸ’» πŸ’¬ ⚠️ πŸ› πŸ’‘ 🎨 πŸ‘€ πŸ€” πŸ“–

Josh Sloat

πŸ› πŸ’» πŸ’‘ πŸ‘€ πŸ€” πŸ“– 🎨 πŸ’¬

Aswin

πŸ’¬

Alex Shelkovskiy

πŸ›

Adrian Bindiu

πŸ›

Kendall Buchanan

πŸ›

Kaycee

πŸ’»

Anuj

πŸ› πŸ’¬

Ludwig Frank

πŸ› πŸ’»

LX

πŸ› πŸ€”

Rosen Tomov

πŸ›

Tom Moor

πŸ’» πŸ›

Johan Preynat

πŸ’» πŸ›

Rahul Gaba

πŸ’» πŸ›

Spencer Davis

πŸ’» πŸ€” πŸ‘€ 🎨

dnlnvl

πŸ’»

Sean King

πŸ€”

Ben Hood

πŸ€” πŸ› πŸ’‘ πŸ‘€

Navilan

πŸ€”

13806

πŸ›

Akshay Kadam (A2K)

πŸ› πŸ€”

Jake Stewart

πŸ› πŸ€”

hhh

πŸ›

@davalapar

πŸ›

Sun Knudsen

πŸ’» πŸ› πŸ€” πŸ’‘ πŸ’¬ πŸ‘€ ⚠️ πŸ“–

Douglas Galdino

πŸ’» πŸ“– πŸ› πŸ€” πŸ’‘ πŸ‘€ ⚠️

Mohammed Faragallah

πŸ› πŸ€” πŸ’‘

Youngrok Kim

πŸ’» πŸ›

Nandhagopal Ezhilmaran

πŸ›

Mattia Astorino

πŸ›

Dan Wood

πŸ“–

Zachery C Gentry

πŸ›

xmflsct

πŸ›

This project follows the all-contributors specification. Contributions of any kind welcome!

GitHub

https://github.com/rpearce/react-medium-image-zoom/
Comments
  • 1. Converted project to typescript

    @rpearce I have converted the project to TS, but there are still 2 things to do. Tests and Storybook are working.

    1st - I wasn't sure about the types for the parentRef portalEl scrollableEl so I was going to leave that to you.

    2nd - In the tsconfig.json file if you have declaration set to true, it generate the d.ts types automatically but they are not very readable in certain cases, so I thought to ask your preference and also leave to you, in case you wanted to build a more custom type file with comments, etc...

    Reviewed by dougg0k at 2020-03-05 17:39
  • 2. Incorrect scale on iOS with constrained unzoomed width

    This issue is a:

    • Bug report

    Expected behavior

    That the zoomed image would scale correctly on all platforms.

    Actual behavior

    On iOS, the zoomed image is scaled based on the rendered size of the unzoomed image and appears blurry.

    The zooming does work as expected on desktop and Android. This issue appears to be related to iOS only.

    Steps to reproduce behavior

    1. Apply a constrained width to an unzoomed image (e.g. 20%)
    2. View the page with an iOS device
    3. Run the linked the example below in the dev server and view it with an iOS device (both Safari and Chrome for iOS exhibit the same behavior).

    Link to repository/example of behavior

    I have patched the example to apply a 20% width style attribute that highlights the behavior - the patch to the upstream is here

    This runs with the pre-configured node dev server.

    Notes

    This example contains the fix from #101, so hopefully this issue is not a duplicate of that.

    I had a go at trying to tweak the transform logic here:

    https://github.com/rpearce/react-medium-image-zoom/blob/25d563e3e0d03eb7cc72e560e7e1e035feff2a1d/src/Zoom.js#L141

    I made some improvements by adding a {zoom: 2} property and halving the scale factor. This appears to make the device render the full resolution. So the image is now clear. But the side effect is that the X,Y coordinate logic pushes the image out of the viewport.

    I saw that a rewrite of the library is in progress, so I decided to first report this issue and provide a means of reproducing the symptom first.

    Reviewed by 0x6e6562 at 2019-01-10 14:46
  • 3. React Portal is incompatible with SSR

    Hi,

    I've been using react-medium-image-zoom in a Gatsby project and while in development it has been working, it doesnt when you try to build your project.

     ERROR #95312 
    
    "document" is not available during server side rendering.
    
    See our docs page for more info on this error: https://gatsby.dev/debug-html
    
    
      68 |     overlayBgColorStart: overlayBgColorStart,
      69 |     parentRef: wrapRef,
    > 70 |     portalEl: portalEl || document.body,
         | ^
      71 |     scrollableEl: scrollableEl || window,
      72 |     transitionDuration: transitionDuration,
      73 |     zoomMargin: zoomMargin,
    
    
      WebpackError: ReferenceError: document is not defined
      
      - Controlled.js:70 Controlled
        node_modules/react-medium-image-zoom/dist/esm/Controlled.js:70:1
    
    

    Btw, I am using the Uncontrolled Zoom.

    I found these 2 packages, that could help:

    • https://github.com/alex-cory/react-useportal
    • https://github.com/jesstelford/react-portal-universal

    But there is another possible solution that you would need to ask the consumer to also install this package if he/she were to use react-medium-image-zoom in Gatsby https://www.gatsbyjs.org/packages/gatsby-plugin-portal/

    Reviewed by dougg0k at 2020-03-03 12:54
  • 4. Add srcset support and pass all props to zoomImage

    This PR addresses issue #42.

    All props are now passed down to the Zoom img component, with special cases for src, srcset, and style.

    One note/question: the hasLoaded state on the Zoom component is being set in two places https://github.com/rpearce/react-medium-image-zoom/blob/master/src/react-medium-image-zoom.js#L259 https://github.com/rpearce/react-medium-image-zoom/blob/master/src/react-medium-image-zoom.js#L297

    Maybe I'm missing something, but they seem to each reference a difference load event - one being the component mounting, the other being the img element loading. Is this right?

    Reviewed by jeremybini at 2017-05-02 17:02
  • 5. Implemented wrapElement option and updated stories

    This option accepts β€œdiv” or β€œspan” and defaults to β€œdiv”.

    This pull request closes https://github.com/rpearce/react-medium-image-zoom/issues/236.

    I never used Storybook. Crossing my fingers I didn’t mess things up. Standby for feedback.

    Pre-Flight Checklist

    I have made sure to

    • [x] add tests to maintain 100% test coverage & add a Storybook example (when this makes sense)
    • [x] manually test all the Storybook examples
    • [x] run $ yarn build to build the project code
    Reviewed by sunknudsen at 2020-03-08 01:36
  • 6. Controlled vs uncontrolled

    Fixes #30 Fixes #49

    • when you pass a non-null value to isZoomed on component instantiation, the component should only zoom/unzoom when that isZoomed prop changes. This means that the consumer is in charge of when it's open or closed. Clicking on the image will not do anything. If it is a controlled component but then you do not pass isZoomed with a non-null value, then an error will be raised telling you that you must control it.
    • if you do not pass true or false to isZoomed, then the component will control itself. This means that if you initially pass isZoomed={null} and then pass isZoomed={true} (or false), then it will throw an error. If it is an uncontrolled component but then you do pass isZoomed with a non-null value, then an error will be raised telling you that you must not control it.

    Please help test the sh*t out of this.

    cc @cbothner and @rajit

    Reviewed by rpearce at 2017-08-13 01:27
  • 7. Feature / Support Request - Slideshow

    This issue is a kind of both:

    • Feature request
    • Question / support request

    Description

    Is there any possibility or example to implement a Slideshow with this package? I have tried this with a controlled component, but when I set the isZoomed property to false on one image and to true on another image, the open and close animations come into play, doesn't look very nice.

    So my Question: Is there any way to open an Image Slider/Colorbox with this beautiful effect?

    Reviewed by Daveiano at 2018-12-15 16:44
  • 8. styles for zoomImage prop don't work

    Expected behavior

    I expected style key to work with the zoomImage prop.

    e.g.

    zoomImage={{
        src: `${item.imageS}`,
        style: {width: `20rem`, height: `5rem`}
    }}
    

    ...makes the zoomed image 20rem wide, 5rem tall. Changing styles works perfectly for the image prop.

    Actual behavior

    Adding style key the zoomImage prop object has no effect.

    e.g.

    zoomImage={{
        src: `${item.imageS}`,
        style: {width: `20rem`, height: `5rem`}
    }}
    
    
    Reviewed by 13806 at 2018-02-17 17:35
  • 9. Combination of next/image with layout responsive

    This issue is a:

    • Question / support request

    Description

    Using next/image so far so great. But when using layout='responsive', the image's width and height are somehow set to 0. Is there any way around? https://nextjs.org/docs/api-reference/next/image#layout

    Reviewed by xmflsct at 2021-04-17 22:41
  • 10. bundle size

    After releasing patch 4.0.2 bundle size grow to 160kb I'm importing as suggested in the readme

    import Zoom from 'react-medium-image-zoom';

    https://bundlephobia.com/result?p=react-medium-image-zoom

    any idea what's wrong and Thank you for this awesome library.

    Reviewed by MohammedFaragallah at 2020-03-01 13:36
  • 11. Element not defined in SSR builds

    This issue is a:

    • Question / support request

    Description

    When this library is included in a Gatsby site, it works fine in development.

    However, when a production build is generated using SSR, the absence of the Element object causes the process to fail:

    1:47:39 PM: failed Building static HTML for pages - 9.002s
    1:47:39 PM: error Building static HTML failed
    1:47:39 PM:   121 |   overlayBgColorStart: string.isRequired,
    1:47:39 PM:   122 |   parentRef: object.isRequired,
    1:47:39 PM: > 123 |   portalEl: instanceOf(Element).isRequired,
    1:47:39 PM:       |                       ^
    1:47:39 PM:   124 |   scrollableEl: oneOfType([object, instanceOf(Element), instanceOf(Window)]).isRequired,
    1:47:39 PM:   125 |   transitionDuration: number.isRequired,
    1:47:39 PM:   126 |   zoomMargin: number.isRequired
    1:47:39 PM: 
    1:47:39 PM:   WebpackError: ReferenceError: Element is not defined
    1:47:39 PM:   
    1:47:39 PM:   - UncontrolledActivated.js:123 Module../node_modules/react-medium-image-zoom/d    ist/esm/UncontrolledActivated.js
    1:47:39 PM:     node_modules/react-medium-image-zoom/dist/esm/UncontrolledActivated.js:123:2    3
    1:47:39 PM:   
    1:47:39 PM:   - index.js:1 Module../node_modules/react-medium-image-zoom/dist/esm/index.js
    1:47:39 PM:     node_modules/react-medium-image-zoom/dist/esm/index.js:1:1
    

    A Gatsby-specific workaround for this is:

    exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
      if (stage === "build-html") {
        actions.setWebpackConfig({
          module: {
            rules: [
              {
                test: /react-medium-image-zoom/,
                use: loaders.null(),
              },
            ],
          },
        })
      }
    }
    

    But when doing so, you also lose the CSS normally imported thusly:

    import 'react-medium-image-zoom/dist/styles.css';
    

    Which breaks the zooming.

    So to work around this, I added the rollup build of the CSS to the Gatsby project and imported it as if it were a local CSS file.

    It seems that the offending functionality is only used in propTypes, so I was wondering whether there is an option in rollup to either

    • Supply some substitute for the Element object for non-browser environments, or
    • Publish a version of the library that strips out the propTypes for SSR purposes (since propTypes are generally only used during development)
    Reviewed by 0x6e6562 at 2020-01-05 23:45
  • 12. Missing class properties transform.

    Hello,

    I've react project and I'm using react-app-rewired, However when I installed this package I get the following error:

    SyntaxError: /path/node_modules/react-medium-image-zoom/dist/index.js: Missing class properties transform.
      329 | }
      330 | class ControlledBase extends Component {
    > 331 |     static defaultProps = {
          |     ^
      332 |         a11yNameButtonUnzoom: 'Minimize image',
      333 |         a11yNameButtonZoom: 'Expand image',
      334 |         IconUnzoom: ICompress,
    ^[[B
    

    I've @babel/plugin-proposal-class-properties installed, here's my babel.config.json

    {
      "presets": [
        "@babel/preset-react",
        "@babel/preset-env"
      ],
      "plugins": [
        "@babel/plugin-transform-react-jsx",
        "@babel/plugin-proposal-class-properties"
      ]
    }
    
    Reviewed by MrLibya at 2022-08-04 14:04
  • 13. Add wrapper or header when image is zoomed in

    • Feature request

    Description

    It would be nice if we could have a header when the image is zoomed in, not to confuse with the caption which is when the image is zoomed out. #242

    I would be really nice if we could have a new prop such as ZoomWrapperComponent that lets you wrap the zoomed in images

    <Zoom
    ...
    ZoomWrapperComponent={children => {
    <div>{children}</div>
    }} >
    

    or at least lets you add a component on top of the image

    <Zoom
    ZoomHeaderComponent={HeaderComponent}>
    

    Thanks

    Reviewed by PaoloDiBello at 2022-08-03 08:36
  • 14. Flicker effect when zooming out

    This issue is a:

    • Bug report

    Expected behavior

    I expected no flicker when the image goes back to its original size

    Actual behavior

    When I click to go back to a zoomed out size, there is a flicker effect

    Steps to reproduce behavior

    1. Click on the image
    2. Click out of the image
    3. Notice the flicker

    Link to repository/example of behavior

    See my personal portfolio

    1. Go to Portfolio
    2. Click on "Work"
    3. Open one of the projects
    4. Click on an image
    5. Click again to zoom out
    6. Notice the flicker

    Code Snippet;

    <Zoom>
    <img className="work-hover-text-content-im" src={`${process.env.PUBLIC_URL}/images/test.png`}/>
    </Zoom>
    

    Technical Details

    • Built with React
    • Chrome & Firefox browsers

    How to solve issue?

    It seems that it's coming from the data-rmiz-wrap property that is set as hidden when the user clicks on the image. If I force the value to be set as visible while zoomed in, I don't get the flicker when zooming out.

    Reviewed by qrospars at 2022-07-18 17:07
React component for desktop browsers for image zoom on mouse hover

react-image-zoom Overview React component for desktop browsers for image zoom on mouse hover. Demo Demo Install npm install react-image-zoom --save U

Aug 5, 2022
A React library for zooming images like Medium

react-medium-zoom A React library for zooming images like Medium Install npm install --save react-medium-zoom Usage import React, { Component } from '

Jul 24, 2021
:eyes: A React component that adds pan and zoom features to SVG

react-svg-pan-zoom react-svg-pan-zoom is a React component that adds pan and zoom features to the SVG images. It helps to display big SVG images in a

Jul 29, 2022
React library to support easy zoom, pan, pinch on various html dom elements like images and divs

react-zoom-pan-pinch Super fast and light react npm package for zooming, panning and pinching html elements in easy way Demo Docs Features ?? Fast and

Jul 30, 2022
A simple slideshow component built with react that supports slide, fade and zoom effects

React-Slideshow A simple slideshow component built with react that supports slide, fade and zoom effects. For full documentation click here Installati

Jul 31, 2022
React-image-viewer - An image viewer example for react.js

React Image Viewer Demo Installation npm i react-image-viewer-dv Usage import {

Jul 26, 2022
🌈 A React image component. Simple realization of image shadow.

?? react-image-shadow A React image component. Simple realization of image shadow.

Feb 10, 2022
A react component that renders image tag only if the image source is found without error.

Img-or-alt This react component that will render image tag only if the image source is found without error, otherwise it renders only the alt text in

Dec 14, 2021
React Image and Background Image Preloader and Fade in. Load those images in smooth!

React Image and Background Image Fade Fade in images AND background images easily in React ?? ! Demo and Docs are live! react-image-and-background-ima

May 24, 2022
Recreate one image using the tiles from another image.
Recreate one image using the tiles from another image.

Mosaic Recreate one image using the tiles from another image. https://mosaic.constraint.systems About the algorithm The image proccessing works like t

Jul 6, 2022
nextjs-image-generation is a POC on how to do server-side image generation.
nextjs-image-generation is a POC on how to do server-side image generation.

nextjs-image-generation is a POC on how to do server-side image generation.

May 29, 2022
React carousel image gallery component with thumbnail support πŸ–Ό
React carousel image gallery component with thumbnail support  πŸ–Ό

React Carousel Image Gallery Live Demo (try it on mobile for swipe support) linxtion.com/demo/react-image-gallery React image gallery is a React compo

Aug 1, 2022
React for Upload Image Gallery. Drag & Drop, Sortable, Customize.
React for Upload Image Gallery. Drag & Drop, Sortable, Customize.

React Upload Gallery A simple library that lets you create an image gallery, change the order of images, select the highlighted image, and customize i

Jul 2, 2022
simple image slider component for react
simple image slider component for react

simple image slider component for react

Aug 2, 2022
A React component for magnifying an image within its original container

A React component for magnifying an image within its original container. Zoom behavior can be triggered on click or hover and the zoomed image can be moved by dragging on touch devices and either dragging or pan on hover on non-touch devices. The component supports responsive images, loading placeholders, optional fullscreen zoom on mobile, and more.

Aug 7, 2022
React Native Image Gallery with Thumbnails
React Native Image Gallery with Thumbnails

React Native Image Gallery with Thumbnails Features Image gallery with thumbnails Made with PanResponder, no external dependencies Written in Typescri

Jul 25, 2022
An interactive, multi-layer image component for React

react-layered-image react-layered-image is an interactive, multi-layer image component for React, inspired by the Apple TV layered images. Features Ru

Jul 5, 2022
React component for image displaying in full screen
React component for image displaying in full screen

React Image Viewer [email protected] has been rewriten with React Hooks and all apis of it have been redesigned. If you are using [email protected], please

Apr 6, 2022
Justified image gallery component for React

React Grid Gallery Justified image gallery component for React inspired by Google Photos and based upon React Images. ?? Maintainers wanted ?? As reac

Aug 1, 2022