Skip to content

feat: add useDevicePixelRatio - #17

Merged
umsungjun merged 6 commits into
mainfrom
feat/use-device-pixel-ratio
Aug 18, 2026
Merged

feat: add useDevicePixelRatio#17
umsungjun merged 6 commits into
mainfrom
feat/use-device-pixel-ratio

Conversation

@umsungjun

Copy link
Copy Markdown
Owner

Added

useDevicePixelRatio(), a hook returning window.devicePixelRatio for @2x/@3x asset selection, canvas backing store scaling, and tile resolution requests.

import { useDevicePixelRatio } from 'react-device-check';

const dpr = useDevicePixelRatio();

Reactive: the value moves on browser zoom, on a display scale change, and when the window crosses screens of different densities. Server render and hydration first paint both report 1, so server and client HTML always match, and the real ratio arrives one render later.

The ratio has its own store instead of a field on DeviceInfo, so useDevice() keeps exactly two listeners and its budget stays flat.

import before after
everything 1.5 kB 1.63 kB
{ useIsMobile } 1.09 kB 1.09 kB
{ useDevice } 1.33 kB 1.33 kB
{ detectDevice } 830 B 830 B
{ useDevicePixelRatio } n/a 609 B

13 new unit tests (85 → 98). E2E asserts the ratio across all six device profiles, reading the expected value from the Playwright descriptor's deviceScaleFactor so the matrix covers 1, 2, 2.5 and 3.

Docs

  • API reference section in both READMEs, plus a row in the website API table. Deliberately not in the hero copy or the feature cards, since reading a ratio involves no detection.
  • Three known limitations: the server cannot know the ratio and no header carries it, browser zoom is indistinguishable from a genuinely denser screen, and Safari before 16 has no resolution media query support so the value never updates there.
  • The ~1.5 kB claim moves to ~1.6 kB everywhere it appears, and the unit test count to 98.
  • Korean copy pass over README.ko.md and website/content/ko.ts: causal links now run through sentence structure rather than connective adverbs, and one sentence-wide bold came out. One claim was corrected on the way: a correction render keeps CLS at zero because it does not touch geometry, not merely because it lands after the fact.
  • CLAUDE.md gains the new modules and the invariant that the ratio stays out of DeviceInfo. Its count-sync instruction now names the website files it was missing.

Also

  • listen/unlisten (the Safari < 14 addListener fallback) moved from core/store.ts to core/media.ts, since both stores need them.
  • *.tsbuildinfo added to .gitignore.

The DPR store needs the same Safari < 14 addListener fallback the device store already carries.
Sharing one copy keeps the legacy branch in a single place.
Returns window.devicePixelRatio through the same SSR contract as the other hooks: server render and hydration first paint both report 1, and the real ratio arrives one render later.
The ratio is reactive because it moves on browser zoom, on a display scale change, and when a window is dragged between screens of different densities.
A resolution media query matches one exact ratio, so the listener re-arms on the new query every time the value moves.
The store lives in its own module rather than in DeviceInfo, which keeps the useDevice budget flat: 609 B when imported alone.
Both examples render the ratio under data-testid="dpr", and the Next.js example captures the first-paint value the way it already captures the device snapshot.
The expected ratio is read from the Playwright descriptor instead of being duplicated in the spec, so the matrix covers 1, 2, 2.5 and 3 and survives a descriptor change.
The rotation test now also asserts the ratio holds, since it tracks display density rather than viewport size.
The hook gets an API reference section in both READMEs and a row in the website API table.
It deliberately stays out of the hero copy, the "what this solves" section, and the feature cards: those carry the detection story, and reading a ratio involves no detection.
Known limitations gain three entries: the server cannot know the ratio, browser zoom is indistinguishable from a denser screen, and Safari before 16 never updates it.
The everything budget measured 1.63 kB after the hook landed, so the ~1.5 kB claim moves to ~1.6 kB everywhere it appears.
Unit test count goes from 85 to 98, and CLAUDE.md's sync instruction now names the website files it was missing.
Applied to README.ko.md and website/content/ko.ts.
Causal links now run through sentence structure instead of the connective adverbs the style guide rules out, and each edit leaves a short sentence next to a long one rather than a chain of even-length ones.
Dropped the sentence-wide bold in the CSS-versus-hook bullet, which is prose rather than a heading, and restated its claim: a correction render keeps CLS at zero because it does not touch geometry, not merely because it happens after the fact.
No structural change, so both READMEs still mirror each other line for line.
website/tsconfig.tsbuildinfo is a local typecheck artifact and does not belong in the tree.
Copilot AI lite review requested due to automatic review settings August 18, 2026 07:17
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-device-check Ready Ready Preview Aug 18, 2026 7:19am

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 95.55556% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/media.ts 66.66% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new useDevicePixelRatio() hook to the library, backed by a dedicated reactive DPR store, so consumers can reactively read window.devicePixelRatio while preserving the existing SSR and bundle-size contracts.

Changes:

  • Introduces src/core/dpr.ts plus useDevicePixelRatio() hook/export, with SSR default of 1 and lazy matchMedia('(resolution: Xdppx)') subscription.
  • Extracts shared matchMedia listener helpers into src/core/media.ts and updates existing stores to reuse them.
  • Updates unit/E2E tests and documentation/website copy to cover the new hook and updated size/test-count claims.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated no comments.

Show a summary per file
File Description
website/tsconfig.tsbuildinfo Adds a TypeScript build info artifact (should not be tracked).
website/lib/seo.ts Updates site description size claim and adds DPR-related keyword.
website/content/en.ts Updates hero badge/test-count copy and adds API row for useDevicePixelRatio().
website/content/ko.ts Updates hero badge/test-count copy and adds API row for useDevicePixelRatio().
src/core/media.ts New shared listen/unlisten helpers for matchMedia change events.
src/core/dpr.ts New DPR reactive store with SSR default 1 and resolution-query listener.
src/useDevicePixelRatio.ts New public hook using useSyncExternalStore (or fallback) wired to the DPR store.
src/index.ts Exports useDevicePixelRatio from the public entrypoint.
src/core/store.ts Switches to shared listen/unlisten helpers (removes local copies).
src/test/dpr.test.ts New unit tests covering DPR store behavior and listener lifecycle.
src/test/helpers.ts Adds stubDevicePixelRatio and dprQuery helpers for DPR tests.
src/test/hooks.test.tsx Adds hook-level tests for useDevicePixelRatio() behavior and non-interference with useDevice().
src/test/setup.ts Resets DPR store between tests to avoid state leakage.
src/test/ssr.test.tsx Adds SSR safety regression test for useDevicePixelRatio() returning 1 on server.
e2e/device-detection.spec.ts Extends Playwright matrix to assert DPR in CSR/SSR flows using deviceScaleFactor.
examples/basic/App.tsx Displays DPR in the CSR example and updates user hint text.
examples/nextjs/app/DeviceDemo.tsx Displays live DPR and captures first-paint DPR (1) in the SSR example.
README.md Documents useDevicePixelRatio(), updates bundle-size/test-count claims, and adds limitations.
README.ko.md Korean docs updates mirroring README changes and adds DPR limitations.
package.json Adds size-limit budget entry for { useDevicePixelRatio } and adds related keywords.
CLAUDE.md Updates architecture/testing guidance and invariants to include the DPR store and hook.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@umsungjun
umsungjun merged commit 2aab7c8 into main Aug 18, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants