Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

React Viewport

Visual viewport geometry as React state.

CSS owns layout. React Viewport exposes geometry; your application decides when that geometry changes behavior. If CSS solves it, don’t install React Viewport.

const { ready, layout, visual, keyboard, safeArea, orientation, supported } = useViewport()

Start with CSS alternatives, then read Keyboard and safe area and Browser behavior.

Quick decision

  • CSS can size or position it: use CSS.
  • You need one occasional value: read window.visualViewport directly.
  • React must react to shared layout/visual geometry: use React Viewport.

Alpha software: 0.1.0-alpha.0 may change. Physical iPhone Safari and Android Chrome testing is pending. Read browser limitations and real-device QA before making a support claim.

Installation

npm install @nipe-solutions/react-viewport

The package has no runtime dependencies. It supports React and React DOM ^18.3.0 || ^19.0.0, ships ESM, CommonJS, and TypeScript declarations, and requires Node.js >=24 <25 for repository development.

Quick start

import { useViewport } from '@nipe-solutions/react-viewport'

export function ViewportReadout() {
  const viewport = useViewport()

  if (!viewport.ready || viewport.visual === null) {
    return <p>Measuring viewport…</p>
  }

  return (
    <p>
      Visible size: {viewport.visual.width} × {viewport.visual.height}; keyboard:{' '}
      {viewport.keyboard.open ? `${viewport.keyboard.height}px` : 'closed'}
    </p>
  )
}

No provider is required. Use ViewportProvider only for a same-origin window scope after verifying that an iframe or popup Window is accessible. Each window gets one shared useSyncExternalStore store and listener set. See the API reference.

Why not window.visualViewport?

Use it directly for occasional reads. React Viewport earns its place when React needs a consistent, reactive snapshot shared across consumers, an SSR-neutral initial snapshot, safe-area values, or normalized keyboard occlusion. It exposes geometry; it does not position elements, manage focus, scroll automatically, define responsive breakpoints, or replace CSS.

Geometry Lab

Test React Viewport on your phone →

Change visual dimensions, offsets, page coordinates, scale, and safe areas; then test rendering budgets and document-coordinate visibility. Copy diagnostics excludes input text. Physical QA remains pending; follow the device protocol. A secondary CSS baseline remains available for layout work.

Reading ViewportState

  • ready becomes true after the first client measurement. Before then, layout, visual, and orientation are null; false does not mean the browser APIs are unsupported.
  • layout is the layout viewport width and height from window.innerWidth and window.innerHeight, in CSS pixels. It is the page's reference plane, not a promise that every point is visible or unobstructed.
  • visual is the visible viewport's size, layout-relative offsets, document-relative page coordinates, and scale. It comes from window.visualViewport when available; otherwise documented layout geometry is used. A visual change does not, by itself, identify its cause as a keyboard.
  • keyboard.open records sufficient native or fallback evidence of an on-screen keyboard. keyboard.height is only bottom-edge occlusion in CSS pixels, not the on-screen keyboard's full rectangle. Native floating geometry can therefore be open with a zero height.
  • safeArea contains the four raw CSS env(safe-area-inset-*) measurements. It does not automatically become zero while a keyboard is visible. For a bottom constraint, use Math.max(keyboard.height, safeArea.bottom); do not add them.
  • orientation is derived from the layout viewport aspect ratio. It is not a device-orientation sensor reading.
  • supported.visualViewport and supported.virtualKeyboard report runtime API availability. They do not prove a behavior was physically tested, that overlay mode is active, or that a keyboard will be detected in every configuration.

Layout viewport versus visual viewport

The Layout viewport is window.innerWidth and window.innerHeight: the coordinate space used for layout. The Visual viewport is the currently visible region. When window.visualViewport is available, it also has offsets, page coordinates, and a scale. A soft keyboard, browser UI, or pinch zoom can change the visual viewport without changing the layout viewport.

The API intentionally keeps those coordinate systems separate:

const { layout, visual, keyboard, safeArea, supported } = useViewport()

On a client without window.visualViewport, visual falls back to layout geometry with zero offsets, page coordinates from window scroll, and scale 1. supported.visualViewport records that this is fallback geometry rather than a native VisualViewport reading.

visual.offsetTop and offsetLeft are layout-relative. visual.pageTop and pageLeft are document-relative CSS pixels. Convert a DOM rectangle by adding window.scrollX and window.scrollY to getBoundingClientRect() values from the same window. Do not multiply by visual.scale; scale is not a breakpoint.

Keyboard and safe area

keyboard.height is the estimated or reported bottom viewport occlusion caused by the software keyboard. It is not the physical keyboard's full rectangular height.

Detection follows a deliberately short hierarchy:

  1. Native Virtual Keyboard intersection geometry is authoritative when available.
  2. Otherwise, conservative VisualViewport inference can report an occlusion.
  3. When the evidence is insufficient, the library reports no keyboard.

The W3C VirtualKeyboard API defines boundingRect as the intersection of the virtual keyboard with the document viewport in client coordinates. supported.virtualKeyboard means that API is present; it does not mean overlaysContent mode is active. This library observes geometry and never enables overlay mode. A non-empty native intersection sets open: true; if floating geometry does not touch the layout viewport's bottom edge, bottom occlusion remains height: 0.

A bottom-attached partial-width rectangle still yields a scalar bottom inset. That scalar cannot represent segmented or arbitrary-shape avoidance.

The fallback infers an occluding software keyboard only when an editable element is focused, zoom is not active, and visual-bottom occlusion crosses max(80 CSS px, 15% of layout height). The keyboard-closed baseline is only an evidence gate: reported fallback height is always the current Math.max(0, layoutHeight - (visualOffsetTop + visualHeight)). If layout and visual height shrink together with no current bottom occlusion, the keyboard remains closed. Focus alone never means that a software keyboard is open. This deliberate heuristic can miss small, floating, or split keyboards; treat keyboard as measured or inferred geometry, not a device-level keyboard guarantee.

Keyboard occlusion and the raw bottom safe-area inset can describe the same covered edge. When an application needs one bottom constraint, use Math.max(keyboard.height, safeArea.bottom) rather than adding them.

CSS variables

For CSS-driven positioning, install variables on the document root (the default) or on a chosen element:

import { useViewportCssVariables } from '@nipe-solutions/react-viewport'

export function App() {
  useViewportCssVariables()
  return <main></main>
}

The hook is a CSS bridge, not a layout system. It writes these client-side variables: layout and visual width/height, visual offsets/page positions/scale, keyboard height, and four safe-area inset lengths. Dimensional variables such as --react-viewport-layout-height are removed until the first measurement, rather than populated with made-up server values. Consumers share one store per window. CSS-variable ownership is restored on cleanup; see Concepts.

Non-zero env(safe-area-inset-*) values generally require the page viewport to opt into viewport-fit=cover. Configure that metadata before relying on the package's measured safeArea values; unsupported or zero-inset environments truthfully report zero.

For a secondary positioning recipe, see the CSS baseline. Prefer direct CSS env() and dynamic viewport units when they solve the layout.

SSR and hydration

SSR uses the stable, geometry-neutral snapshot required by useSyncExternalStore without accessing browser globals. Render a placeholder until ready becomes true after hydration.

When CSS is enough

Prefer CSS when the browser can express the behavior directly. Use dvh, svh, and lvh for viewport-relative sizing; use env(safe-area-inset-*) for safe area padding; and use media/container queries for responsive layout. This library is for React behavior that needs measured geometry or an explicit layout-versus-visual distinction. It is not a breakpoint, device-detection, scroll-locking, focus-management, modal, or general mobile-layout library.

Browser terminology and limitations

Supported means the runtime can detect an API on the current browser. Tested means a deterministic repository scenario covers a behavior in the configured Chromium, Firefox, or WebKit projects. Fallback means the package uses documented alternate geometry when an optional API is absent. These labels are different from physical-device verification.

The project does not claim universal browser support. In particular:

  • Browser APIs cannot reliably distinguish all floating or split software-keyboard arrangements.
  • The fallback inference intentionally favors false negatives over moving UI for ordinary browser chrome changes.
  • Desktop automation cannot reproduce physical mobile browser chrome or keyboard animations exactly.
  • Embedded WebViews can expose different viewport behavior and need host-level verification.
  • Foldable viewport segments and synthetic keyboard animations are outside v1.

See docs/browser-notes.md for the browser-note registry and docs/REAL_DEVICE_QA.md for the current physical-device matrix.

Project

Part of NIPE Open Source

About

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages