TypeScript icon, indicating that this package has built-in type declarations

    1.0.6 • Public • Published



    A JavaScript library for zooming images like Medium

    version MIT license downloads
    gzip size no dependencies travis

    Medium Zoom Demo

    🔬 Playground🔎 Demo📚 Storybook



    • 📱 Responsivescale on mobile and desktop
    • 🚀 Performant and lightweightshould be able to reach 60 fps
    • ⚡️ High definition supportload the HD version of your image on zoom
    • 🔎 Flexibilityapply the zoom to a selection of images
    • 🖱 Mouse, keyboard and gesture friendlyclick anywhere, press a key or scroll away to close the zoom
    • 🎂 Event handlingtrigger events when the zoom enters a new state
    • 📦 Customizationset your own margin, background and scroll offset
    • 🔧 Pluggableadd your own features to the zoom
    • 💎 Custom templatesextend the default look to match the UI of your app


    The module is available on the npm registry.

    npm install medium-zoom
    # or 
    yarn add medium-zoom


    Try it out in the browser

    Import the library as a module:

    import mediumZoom from 'medium-zoom'

    Or import the library with a script tag:

    <script src="node_modules/medium-zoom/dist/medium-zoom.min.js"></script>

    That's it! You don't need to import any CSS styles.

    Assuming you add the data-zoomable attribute to your images:



    mediumZoom(selector?: string | HTMLElement | HTMLElement[] | NodeList, options?: object)Zoom


    The selector allows attaching images to the zoom. It can be of the following types:

    // CSS selector
    // HTMLElement
    // NodeList
    // Array
    const images = [


    The options enable the customization of the zoom. They are defined as an object with the following properties:

    Property Type Default Description
    margin number 0 The space outside the zoomed image
    background string "#fff" The background of the overlay
    scrollOffset number 40 The number of pixels to scroll to close the zoom
    container string | HTMLElement | object null The viewport to render the zoom in
    Read more →
    template string | HTMLTemplateElement null The template element to display on zoom
    Read more →
    mediumZoom('[data-zoomable]', {
      margin: 24,
      background: '#BADA55',
      scrollOffset: 0,
      container: '#zoom-container',
      template: '#zoom-template',


    open({ target?: HTMLElement }): Promise<Zoom>

    Opens the zoom and returns a promise resolving with the zoom.

    const zoom = mediumZoom('[data-zoomable]')

    Emits an event open on animation start and opened when completed.

    close(): Promise<Zoom>

    Closes the zoom and returns a promise resolving with the zoom.

    const zoom = mediumZoom('[data-zoomable]')

    Emits an event close on animation start and closed when completed.

    toggle({ target?: HTMLElement }): Promise<Zoom>

    Opens the zoom when closed / dismisses the zoom when opened, and returns a promise resolving with the zoom.

    const zoom = mediumZoom('[data-zoomable]')

    attach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom

    Attaches the images to the zoom and returns the zoom.

    const zoom = mediumZoom()
    zoom.attach('#image-1', '#image-2')

    detach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom

    Releases the images from the zoom and returns the zoom.

    const zoom = mediumZoom('[data-zoomable]')
    zoom.detach('#image-1', document.querySelector('#image-2')) // detach two images
    zoom.detach() // detach all images

    Emits an event detach on the image.

    update(options: object): Zoom

    Updates the options and returns the zoom.

    const zoom = mediumZoom('[data-zoomable]')
    zoom.update({ background: '#BADA55' })

    Emits an event update on each image of the zoom.

    clone(options?: object): Zoom

    Clones the zoom with provided options merged with the current ones and returns the zoom.

    const zoom = mediumZoom('[data-zoomable]', { background: '#BADA55' })
    const clonedZoom = zoom.clone({ margin: 48 })
    clonedZoom.getOptions() // => { background: '#BADA55', margin: 48, ... }

    on(type: string, listener: () => void, options?: boolean | AddEventListenerOptions): Zoom

    Registers the listener on each target of the zoom.

    The same options as addEventListener are used.

    const zoom = mediumZoom('[data-zoomable]')
    zoom.on('closed', event => {
      // the image has been closed
      event => {
        // the image has been opened (tracked only once)
      { once: true }

    The zoom object is accessible in event.detail.zoom.

    off(type: string, listener: () => void, options?: boolean | AddEventListenerOptions): Zoom

    Removes the previously registered listener on each target of the zoom.

    The same options as removeEventListener are used.

    const zoom = mediumZoom('[data-zoomable]')
    function listener(event) {
      // ...
    zoom.on('open', listener)
    // ...'open', listener)

    The zoom object is accessible in event.detail.zoom.

    getOptions(): object

    Returns the zoom options as an object.

    const zoom = mediumZoom({ background: '#BADA55' })
    zoom.getOptions() // => { background: '#BADA55', ... }

    getImages(): HTMLElement[]

    Returns the images attached to the zoom as an array of HTMLElements.

    const zoom = mediumZoom('[data-zoomable]')
    zoom.getImages() // => [HTMLElement, HTMLElement]

    getZoomedImage(): HTMLElement

    Returns the current zoomed image as an HTMLElement or null if none.

    const zoom = mediumZoom('[data-zoomable]')
    zoom.getZoomedImage() // => null => {
      zoom.getZoomedImage() // => HTMLElement



    Specifies the high definition image to open on zoom. This image loads when the user clicks on the source image.

    <img src="image-thumbnail.jpg" data-zoom-src="image-hd.jpg" alt="My image" />


    Event Description
    open Fired immediately when the open method is called
    opened Fired when the zoom has finished being animated
    close Fired immediately when the close method is called
    closed Fired when the zoom out has finished being animated
    detach Fired when the detach method is called
    update Fired when the update method is called
    const zoom = mediumZoom('[data-zoomable]')
    zoom.on('open', event => {
      // track when the image is zoomed

    The zoom object is accessible in event.detail.zoom.


    Trigger a zoom from another element
    const button = document.querySelector('[data-action="zoom"]')
    const zoom = mediumZoom('#image')
    button.addEventListener('click', () =>
    Track an event (for analytics)

    You can use the open event to keep track of how many times a user interacts with your image. This can be useful if you want to gather some analytics on user engagement.

    let counter = 0
    const zoom = mediumZoom('#image-tracked')
    zoom.on('open', event => {
      console.log(`"${}" has been zoomed ${++counter} times`)
    Detach a zoom once closed
    const zoom = mediumZoom('[data-zoomable]')
    zoom.on('closed', () => zoom.detach(), { once: true })
    Attach jQuery elements

    jQuery elements are compatible with medium-zoom once converted to an array.

    Create a zoomable React component

    Using React hooks

    import React from 'react'
    import mediumZoom from 'medium-zoom'
    function ImageZoom({ zoom, src, alt, background }) {
      const zoomRef = React.useRef(zoom.clone({ background }))
      function attachZoom(image) {
      return <img src={src} alt={alt} ref={attachZoom} />
    function App() {
      const zoom = React.useRef(mediumZoom({ background: '#000', margin: 48 }))
      render() {
        return (
          <ImageZoom src="image.jpg" alt="Image" zoom={zoom.current} color="#BADA55" />

    Using React classes

    import React, { Component } from 'react'
    import mediumZoom from 'medium-zoom'
    class ImageZoom extends Component {
      zoom = this.props.zoom.clone({
        background: this.props.color,
      attachZoom = image => {
      render() {
        return (
          <img src={this.props.src} alt={this.props.alt} ref={this.attachZoom} />
    class App extends Component {
      zoom = mediumZoom({ background: '#000', margin: 48 })
      render() {
        return (
          <ImageZoom src="image.jpg" alt="Image" zoom={this.zoom} color="#BADA55" />

    You can see more examples including React and Vue, or check out the storybook.

    Browser support

    IE Edge Chrome Firefox Safari
    10* 12* 36 34 9

    * These browsers require a template polyfill when using custom templates.

    Cross-browser testing is sponsored by



    • Run yarn to install Node dev dependencies
    • Run yarn start to build the library in watch mode
    • Run yarn run storybook to see your changes at http://localhost:9001

    Please read the contributing guidelines for more detailed explanations.

    You can also use npm.


    MIT © François Chalifour


    npm i medium-zoom

    DownloadsWeekly Downloads






    Unpacked Size

    80.7 kB

    Total Files


    Last publish


    • francoischalifour