liquid-glass: a React lens that refracts live DOM, and the one feature Chromium keeps
Apple-style Liquid Glass for the web — a headless React lens that refracts the live DOM in Safari, Firefox and Chrome. Zero dependencies.
At a glance
- What is it?
- liquid-glass is a zero-dependency React lens that bends real DOM through an SVG displacement filter instead of rasterising a screenshot. The catch is stated by the project itself: frosting works everywhere, but bending the live page needs backdrop-filter with a url(), which ships in Chrome and Edge only.
- Who is it for?
- Use liquid-glass if you want refraction over real, selectable, clickable content and you cannot accept a screenshot-based approach. Decide up front which of the three modes you need, because in-place bending of live DOM works only in Chrome and Edge, while the refract and WebGL modes are what carry Safari and Firefox.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 111 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Live-page bending needs backdrop-filter with a url(), which is Chromium-only
The library's argument against its neighbours is precise. Most liquid glass implementations use `backdrop-filter: url()`, which only works in Chromium, so they degrade to a flat blur in Safari and Firefox. The WebGL ones rasterise an `html2canvas` screenshot first, which means the text under the glass is frozen and stale. This one runs an SVG displacement filter on the element itself through `filter: url()`, so it refracts the live DOM and keeps text selectable and links clickable.
Then the project states its own limit in the same breath. Bending the live page uses `backdrop-filter: url()`, which ships in Chrome and Edge only. In Safari and Firefox a wrapped element still frosts, tints and edge-lights, so it reads as glass, but it cannot bend the live page.
The README attributes this to web-platform physics rather than a design choice. That distinction matters when you are debugging: the same filter feature that makes the effect work across browsers is the one that confines live bending to Chromium.
Three modes, and the copy mode is what carries Safari and Firefox
The package installs with react and react-dom as peer dependencies, and the basic case is wrapping a styled box:
npm i @samasante/liquid-glassimport { Glass } from "@samasante/liquid-glass";
<Glass style={{ background: "rgba(248,113,113,0.4)", borderRadius: 14, padding: "12px 22px" }}>
Save
</Glass>;The answer to the browser gap is that you tell the lens what to bend. There are three modes.
In-place mode gives the element a geometry with `size` and `center`, and it bends its own children, so a hero or a card refracts itself with no copy to manage. Refract mode takes the node you want to copy and floats a lens over content it does not own, which is the pattern for a panel over a photo or a loupe over a page. The `behind` prop fills the bleed edge, left as auto it derives from the page background, and setting it to `transparent` opts out.
The third mode is WebGL. Passing `src` for a video URL or `draw` for a per-frame canvas painter refracts live media that an SVG filter cannot reach, and one renderer samples the medium and draws many lenses over it, so each control of a video player can be its own lens.
One rule holds across all three: `children` are always the crisp layer on top of the refraction.
A signed-distance field becomes a displacement map, then a 3-pass RGB split
The mechanism is a rasterised description of a rounded rectangle. A rounded-rect signed-distance field is turned into a displacement map where the red and green channels encode X and Y displacement and the blue channel carries a specular mask. That map feeds an SVG `feDisplacementMap` applied to the content through `filter: url(#...)`, with a three-pass RGB split used to produce the chromatic aberration at the edges.
The performance argument is in how geometry is updated. Geometry is driven by lightweight motion values written imperatively each frame, so a lens can follow the pointer or animate at 60fps without re-rendering React. Sizing and styling stay in ordinary CSS through `className`, `style` or Tailwind, and the look is changed through `optics` rather than through a config file. There is no provider to mount and no stylesheet to import.
The hard part is Safari, and the fixes are built in rather than left to you: a single-times filter, shape-only map regeneration, and cache-busting filter ids. `BROWSERS.md` carries the details and the support matrix.
Safari is forced to 1x filter resolution, and self-animating content needs the live path
Two props exist for the same reason: Chromium and Safari do not agree about filter cost. `filterResolution` defaults to 1, is documented as a Chromium-only supersample where 2 is crisper, and is forced to 1 in Safari regardless of what you pass.
That is the compatibility tax stated in the API table. On Chromium you can supersample the displacement map and get a sharper edge. On Safari the value is ignored and you get the single-times path, which is also what the built-in WebKit workaround uses.
The `live` prop is the other half. It defaults to false and, when set, re-rasterizes every frame for content that animates itself, which the README scopes to Safari. So a video lens or a slider thumb driven by its own animation needs the per-frame path enabled, and a static element does not. Size, radius and the centre coordinates are the props most often animated, and each accepts either a plain number or a motion value, which the README defines as anything exposing `get()` and `on('change', cb)`, so a framer-motion value works directly.
The optics vocabulary gates itself, and part of it is DOM-only
`optics` takes one partial object of a shared `GlassOptics` vocabulary, and the defaults are described as balanced. Several of the parameters are not independent. `strength` is the most a pixel moves as a fraction of the box between 0 and 1, with `scaleX` and `scaleY` overriding it per axis. `depth` sets how far the bend reaches in from the edge and also gates `curvature`, so at a low depth the centre of the lens stays flat. `bendWidth` is a fraction of the smaller of width and height with a default of 0.16, and `bend` at 0 makes the lens a plain magnifier. `dispersion` controls the colour split at the edges.
The surface effects are grouped the same way: `sheen`, `sheenWidth`, `sheenFalloff` and `sheenAngle` describe a directional edge highlight with `specular` as the overall gain, while `glow`, `glowSpread` and `glowFalloff` describe a soft inner glow. `frost` is the blurred layer, `brightness` the veil and `splay` the corner spread.
One asymmetry is called out explicitly: `sheenDark` and the shadow fields apply to the DOM element only, so an optics object written for the React lens does not carry over completely to the WebGL surface.
The package is ESM only, and the peer range is wider than the dev range
The manifest is small and specific. The published name is `@samasante/liquid-glass` at version 0.1.1, MIT licensed, with `sideEffects: false` so bundlers can drop unused exports, and a files list containing only `dist`, `README.md` and `LICENSE`.
The entry points matter more than the version. Both `main` and `module` point at the same file, `./dist/index.js`, and the exports map offers a `types` condition and an `import` condition. There is no `require` condition anywhere, which means a CommonJS consumer has nothing to load. For a library with zero runtime dependencies, the only requirement on your side is an ESM-aware build.
The dependency ranges are deliberately asymmetric. Peer dependencies ask for `react` and `react-dom` at 18 or newer, while the development dependencies develop against React 19. So the floor is React 18 while the code is exercised on React 19.
Tooling around it is small as well: tsup builds, `tsc --noEmit` typechecks, prettier formats, and `prepublishOnly` runs the build. The package manager field pins pnpm at 10.28.2.
Five example components, a docs site, and no GitHub releases
The repository holds 17 root entries: `src/`, `examples/`, `docs/`, `site/`, `BROWSERS.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, `LICENSE`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `.prettierrc`, `.prettierignore` and `.gitignore`. The MIT claim in the manifest is backed by a licence file at the root, and the README links it.
The examples are named for the components people actually build: a context menu, a notification panel, a slider, a switch and video controls. Two of them are pointed at directly in the README as copy-paste starting points, the notification panel over a photo and the video controls with lenses over a video element.
The `site/` directory is where the live demo and documentation at `glass.samasante.com` comes from. Note that the homepage appears in the package manifest while repository metadata lists no homepage at all.
The version and the release history are the weakest points. The manifest says 0.1.1, the repository has no GitHub releases and no tags to pin against, and the last push was on 2026-06-23 without the repository being archived.
Editorial conclusion
Use liquid-glass if you want refraction over real, selectable, clickable content and you cannot accept a screenshot-based approach. Decide up front which of the three modes you need, because in-place bending of live DOM works only in Chrome and Edge, while the refract and WebGL modes are what carry Safari and Firefox. Before shipping, check that your build consumes ESM, since the package exposes an import condition only, and read BROWSERS.md, which holds the support matrix and the WebKit-specific workarounds.
Frequently asked questions
How do I install liquid-glass and render my first Glass element?
Run `npm i @samasante/liquid-glass`, where react and react-dom are peer dependencies, then import the `Glass` component and wrap any styled box in it. There is no provider to mount, no stylesheet to import and no config file. Your translucent colour becomes the tint and the children render crisp on top, with sizing and styling left to ordinary CSS.
Does liquid-glass bend the live page in Safari and Firefox?
No. Bending the live page uses `backdrop-filter: url()`, which ships in Chrome and Edge only, so in Safari and Firefox a wrapped Glass still frosts, tints and edge-lights but does not bend. To bend there you refract a copy with the `refract` prop, or use the WebGL `src` and `draw` mode for video and canvas.
What do the optics settings in liquid-glass control?
`strength` is the most a pixel moves as a fraction of the box, `depth` sets how far the bend reaches from the edge and gates `curvature`, and `bend` with `bendWidth` controls the rim refraction with a default band of 0.16 of the smaller side. `dispersion` gives the colour split, the `sheen` and `glow` groups the edge highlight and inner light, and `frost`, `brightness` and `splay` handle the blur, veil and corners. `sheenDark` and the shadow fields apply to the DOM lens only.
What are the peer dependency and module requirements for liquid-glass?
Peer dependencies are `react` and `react-dom` at 18 or newer, with development against React 19. The package has zero runtime dependencies and ships ESM only: both `main` and `module` point at `dist/index.js` and the exports map has an `import` condition with no `require` entry.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/samasante-liquid-glass)