react-medium-image-zoom
Accessible medium.com-style image zoom for React, built on the native dialog element with zero dependencies
Repository Health
Technical Analysis
react-medium-image-zoom is a lightweight React component that recreates the click-to-enlarge image zoom popularized by Medium.com, letting any image, background-image div, picture, figure, or SVG expand into a full-screen, dismissible view. It ships as both an Uncontrolled component that manages its own open/closed state and a Controlled component for apps that need to drive the zoom state externally, such as syncing with a gallery’s active-slide index.
Rather than reimplementing modal and focus-management logic, the library builds on the native dialog element and ResizeObserver, giving it built-in accessibility (focus trapping, Escape-to-close) and screen-reader support that’s been tested against JAWS, NVDA, VoiceOver, and TalkBack. It has zero runtime dependencies beyond React itself, supports swipe-to-unzoom gestures, and exposes a ZoomContent prop for fully custom modal content such as captions.
What You Get
- Uncontrolled and Controlled component variants for self-managed or externally-driven zoom state
- Support for img, background-image div/span, picture, figure, and svg elements out of the box
- Built-in accessibility via the native dialog element, tested against JAWS, NVDA, VoiceOver, and TalkBack
- Customizable zoom modal content via the ZoomContent render prop, plus swipe-to-unzoom gesture support
Common Use Cases
- Blog and documentation sites letting readers zoom into screenshots or diagrams without leaving the page
- Product and portfolio galleries where thumbnails expand to full-size on click
- Photography sites needing an accessible lightbox-style viewer without a heavyweight gallery dependency
- Next.js and Gatsby image pipelines (gatsby-plugin-image, next/image) that need zoom added on top of optimized images
Under The Hood
Architecture
The component is built around a single core class-based ControlledBase (in controlled.tsx) that manages a state machine (ModalState: LOADED/LOADING/UNLOADED/UNLOADING) for the dialog lifecycle, wrapped by two public entry points: Controlled (a thin wrapper around ControlledBase) and Uncontrolled (a functional component holding its own isZoomed state via useState/useCallback that delegates rendering to Controlled). It renders into a native <dialog> element found or created via getDialogContainer() (a singleton [data-rmiz-portal] div appended to document.body) using ReactDOM.createPortal, and observes the original element via ResizeObserver and DOM queries against an IMAGE_QUERY selector that matches <img>, <svg>, [role="img"], and [data-zoom] children. All positioning math is factored into pure utility functions under src/utils/ (get-style-modal-img.ts, compute-positioned-style.ts, get-scale.ts, get-target-dimension.ts, get-modal-img-transform.ts), so controlled.tsx handles DOM refs and lifecycle while geometry calculations stay isolated and testable.
Tech Stack
TypeScript makes up the vast majority of the codebase, targeting ES2021 and built via tsc through tsconfig.build.json. The only runtime dependencies are React and ReactDOM, declared as peer dependencies supporting a wide range of React versions. It relies on native <dialog> and ResizeObserver browser APIs rather than polyfills. Development tooling is modern: oxlint/oxlint-tsgolint for linting, oxfmt for formatting, vitest with happy-dom for testing, storybook for the interactive documentation site, and changesets for versioned releases via pnpm. It ships as ESM only, with a dedicated script that verifies server-side-render safety before publishing.
Code Quality
Test coverage is extensive: nearly every utility function has a co-located test file, alongside a substantial test suite for the main controlled component covering interactive behavior, all run via vitest with a happy-dom DOM environment. Types are strict throughout, with fully documented exported prop interfaces. Error handling is necessarily light for a presentational UI library, but defensive checks guard DOM queries. Naming is consistent and descriptive, and a combined CI script enforces linting, formatting, type-checking, tests, and build together before publish.
What Makes It Unique
The library’s distinguishing choice is building the zoom/dialog behavior on the native <dialog> element and ResizeObserver instead of a hand-rolled modal-and-portal stack with manual resize listeners, which gives it built-in focus-trapping, Escape-to-close, and top-layer stacking directly from the browser. It supports zooming a wide range of content shapes through one shared detection layer, and offers fully swappable modal content via a render-prop for custom captions or UI, a level of customization most comparable libraries don’t expose. It also carries zero runtime dependencies beyond React.
Used by 6 apps in this directory
Dub
Marketing · Analytics
The open-source link attribution platform for short links, conversion tracking, and affiliate programs — powering 100M+ clicks monthly.
Flowsint
Automation · Developer Tools
A privacy-first, graph-based OSINT investigation platform with 30+ automated enrichers for mapping relationships between domains, IPs, people, and organizations.
LLM Gateway
AI Development · Devops
One API endpoint for 25+ LLM providers — route, track costs, enforce compliance, and switch models without changing your code.
medusa
Ecommerce
The most flexible open-source commerce platform — build B2C, B2B, and marketplace applications with modular, composable commerce primitives.
OpenStatus
Monitoring · Devops
Open-source status pages and uptime monitoring with global synthetic checks, monitoring-as-code, and AI agent integration via MCP.
Supabase
Developer Tools · Databases · Search
The open-source Postgres development platform that replaces Firebase with authentication, real-time APIs, edge functions, storage, and vector embeddings — all built on PostgreSQL.