The address bar and toolbar no longer clip your 100vh layouts. The variables always reflect the real visible area.
--vh and --vw for 1% units, --dvh and --dvw for full pixel values. Use whichever fits your CSS.
Uses the modern window.visualViewport for accurate measurements, with innerHeight fallback.
Debounced resize and orientation change listeners keep the variables in sync. Configurable delay (default 100ms).
Add a custom prefix like prefix: 'app-' to avoid conflicts. Only inject the variables you need.
Pure TypeScript. SSR safe. Drop it into any framework or vanilla project. ~0.7kB gzipped.
On the left, height: 100vh extends behind the browser chrome. On the right, height: calc(var(--vh) * 100) fits perfectly.
npm install viewport-units-fix
yarn add viewport-units-fix
pnpm add viewport-units-fix
<script type="module"> import ViewportUnitsFix from 'https://esm.sh/viewport-units-fix'; new ViewportUnitsFix(); </script>
import ViewportUnitsFix from 'viewport-units-fix'; new ViewportUnitsFix(); // That's it. --vh, --vw, --dvh, --dvw are now on :root
import ViewportUnitsFix from 'viewport-units-fix'; const vf = new ViewportUnitsFix({ prefix: 'app-', // --app-vh, --app-vw, etc. debounce: 50, // faster updates variables: ['vh'], // only inject --vh onUpdate: (values) => { console.log(values.dvh); // full height in px }, });
import { useEffect } from 'react'; import ViewportUnitsFix from 'viewport-units-fix'; function useViewportUnits() { useEffect(() => { const vf = new ViewportUnitsFix(); return () => vf.destroy(); }, []); }
/* Full viewport height — the fix for 100vh */ .hero { height: calc(var(--vh) * 100); } /* Or use the full-pixel value directly */ .sidebar { height: var(--dvh); } /* Half viewport */ .panel { height: calc(var(--vh) * 50); }
| Option | Type | Default | Description |
|---|---|---|---|
| variables | ViewportVariable[] | all | Which CSS variables to inject: 'vh', 'vw', 'dvh', 'dvw'. |
| prefix | string | '' | Prefix for CSS variable names (e.g. 'app-' → --app-vh). |
| debounce | number | 100 | Debounce delay in ms. Set to 0 for immediate updates. |
| target | HTMLElement | string | document.documentElement | Element or CSS selector to set CSS variables on. |
| onUpdate | (values: ViewportValues) => void | — | Called after each measurement update. |
| Member | Type | Description |
|---|---|---|
| .values | ViewportValues | Read-only snapshot: { vh, vw, dvh, dvw }. |
| .refresh() | void | Force an immediate recalculation and CSS variable update. |
| .destroy() | void | Remove all event listeners and clean up. |
| [Symbol.dispose]() | void | Alias for destroy(). Enables using syntax. |
| Variable | Value | CSS Usage |
|---|---|---|
| --vh | {height/100}px | height: calc(var(--vh) * 100) |
| --vw | {width/100}px | width: calc(var(--vw) * 100) |
| --dvh | {height}px | height: var(--dvh) |
| --dvw | {width}px | width: var(--dvw) |