Description:
Liquid Glass is a headless React component that applies realistic glassmorphism effects to live DOM elements.
An SVG displacement filter bends the page behind the glass, keeping text selectable and links clickable in Chrome, Safari, and Firefox with zero runtime dependencies.
You can use it for glass buttons, frosted cards, notification panels that refract a background image, or video player controls where every button is its own lens bending the footage.
Preview

Features
- Renders live, interactive glass that bends the real DOM with an SVG displacement map.
- Works in Chrome, Safari, and Firefox, No fallback to a flat blur for the default frost.
- Multiple refraction modes: live DOM, a copy of any node, and WebGL for video/canvas.
- Ships with a headless, composable API. Wrap any styled box and it turns into glass.
- Provides optics controls for strength, depth, curvature, dispersion, bend, sheen, glow, and frost.
- Includes motion utilities for pointer‑driven lenses, velocity squash‑stretch, and framer‑motion‑compatible values.
How To Use It
Installation
Install the package with npm:
npm install @samasante/liquid-glassreact and react-dom are peer dependencies. The package does not require a CSS import, provider, or root-level setup. In Next.js App Router projects, place Glass inside a Client Component because it measures layout and uses browser APIs.
Basic Usage
A bare Glass wrapper creates a frosted, tinted material. Its children stay sharp above the glass layer.
"use client";
import { Glass } from "@samasante/liquid-glass";
export function BillingButton() {
return (
<Glass
className="inline-flex"
style={{
background: "rgba(15, 23, 42, 0.42)",
borderRadius: 14,
padding: "12px 18px",
}}
optics={{ frost: 2, dispersion: 0.28 }}
>
<button type="button" className="font-medium text-white">
Update plan
</button>
</Glass>
);
}This material mode frosts, tints, and highlights the wrapper in Chrome, Edge, Safari, and Firefox. Refraction of arbitrary content behind a fixed panel relies on Chromium support for backdrop-filter: url(). Use an explicit lens or a refract copy when Safari and Firefox need visible refraction too.
More Examples
Move a lens across wrapped DOM content
Pass size and center when the lens should bend content inside its own wrapper. glassValue() avoids React re-renders for pointer movement.
"use client";
import { useMemo } from "react";
import { Glass, glassValue } from "@samasante/liquid-glass";
export function FeatureLens() {
const lensX = useMemo(() => glassValue(0.5), []);
const lensY = useMemo(() => glassValue(0.5), []);
return (
<Glass
size={180}
radius={90}
center={{ x: lensX, y: lensY }}
className="relative block overflow-hidden rounded-3xl"
optics={{
strength: 0.1,
depth: 0.9,
curvature: 0.34,
dispersion: 0.3,
frost: 0.7,
}}
onPointerMove={(event) => {
const bounds = event.currentTarget.getBoundingClientRect();
lensX.set((event.clientX - bounds.left) / bounds.width);
lensY.set((event.clientY - bounds.top) / bounds.height);
}}
>
<section className="min-h-[320px] bg-slate-950 p-10 text-white">
<p className="text-sm text-slate-300">Workspace analytics</p>
<h2 className="mt-4 max-w-md text-4xl font-semibold">
Review product activity from one screen.
</h2>
</section>
</Glass>
);
}center.x and center.y accept numbers or compatible motion values. A supported motion object exposes get() and on("change", callback), which includes the package’s own signals and Framer Motion values.
Build a refracted notification over a photo
Use refract for a floating element that needs to bend a copy of content behind it. The children remain the crisp foreground layer.
import { Glass } from "@samasante/liquid-glass";
const cityPhoto = (
<img
src="/images/city-at-night.jpg"
alt=""
aria-hidden="true"
className="h-full w-full object-cover"
/>
);
export function PhotoNotification() {
return (
<section className="relative h-80 overflow-hidden rounded-3xl">
{cityPhoto}
<Glass
refract={cityPhoto}
behind="#111827"
width={380}
height={124}
radius={24}
className="absolute bottom-6 left-6 p-5 text-white"
optics={{
strength: 0.075,
depth: 0.82,
curvature: 0.22,
dispersion: 0.28,
frost: 0.5,
}}
>
<p className="text-sm text-slate-200">Build finished</p>
<h2 className="mt-1 text-lg font-semibold">
Preview deployment is ready.
</h2>
</Glass>
</section>
);
}Set behind when the refracted copy sits over photography, gradients, or another background with no predictable solid edge color. It fills the small sample area outside the copied node and prevents warped edge artifacts.
Add lenses to a video control layer
Use src for video and lenses for multiple glass controls over one media surface. The component exposes the underlying <video> through videoRef.
"use client";
import { useRef, useState } from "react";
import {
Glass,
type GlassSurfaceLens,
} from "@samasante/liquid-glass";
const transportLenses: GlassSurfaceLens[] = [
{ x: 0.3, y: 0.5, w: 52, h: 52, radius: 26 },
{ x: 0.5, y: 0.5, w: 84, h: 84, radius: 42 },
{ x: 0.7, y: 0.5, w: 52, h: 52, radius: 26 },
];
export function ProductVideo() {
const videoRef = useRef<HTMLVideoElement>(null);
const [paused, setPaused] = useState(true);
return (
<Glass
src="/media/product-tour.mp4"
poster="/media/product-tour-poster.jpg"
videoRef={videoRef}
paused={paused}
muted
loop
lenses={transportLenses}
className="relative block aspect-video overflow-hidden rounded-3xl"
optics={{
depth: 0.9,
curvature: 0.4,
dispersion: 0.24,
frost: 0.35,
}}
>
<button
type="button"
className="absolute left-1/2 top-1/2 h-[84px] w-[84px] -translate-x-1/2 -translate-y-1/2 rounded-full text-sm font-semibold text-white"
onClick={() => setPaused((current) => !current)}
>
{paused ? "Play" : "Pause"}
</button>
</Glass>
);
}Use crossOrigin="anonymous" or crossOrigin="use-credentials" for remote video sources that need to enter the WebGL texture pipeline.
Props and API Reference
<Glass> props
| Prop | Type | Purpose |
|---|---|---|
children | ReactNode | Crisp interface layer above the refracted surface. |
refract | ReactNode | Content copied into the refraction layer. |
behind | string | Solid color used beyond the edge of a copied refraction target. |
width, height | Number or motion value | Lens dimensions in pixels. |
size | Number or [width, height] | Shorthand for lens dimensions. |
radius | Number or motion value | Corner radius in pixels. |
center | { x, y } | Lens center as a 0 to 1 fraction of the wrapper. |
optics | Partial<GlassOptics> | Refraction, blur, lighting, and edge behavior. |
live | boolean | Refreshes animated refracted DOM content frame by frame. |
filterResolution | number | Chromium supersampling value. Safari forces this to 1. |
src | string | Video source for WebGL refraction. |
draw | (ctx, time) => void | Per-frame canvas drawing callback. |
lenses | GlassSurfaceLens[] | Multiple lenses over a shared video or canvas surface. |
videoRef | React ref | Ref to the internally rendered video element. |
paused | boolean | Controlled video playback state. |
poster, loop, muted, autoPlay | Standard media props | Video configuration. |
crossOrigin | "anonymous" or "use-credentials" | Cross-origin video texture configuration. |
maxDpr | number | Maximum device pixel ratio for WebGL buffers. |
overlay | ReactNode | Additional refracted layer for advanced compositions. |
onLensMapChange | Callback | Receives updated displacement-map URLs. |
unstable_lens | Object | Low-level lens controls that may change before version 1.0. |
The component also exposes advanced fields such as depth, scale, brightnessInFilter, and pixelUnits for custom lens implementations.
optics reference
| Option | Purpose |
|---|---|
strength | Sets the primary refraction amount. |
scaleX, scaleY | Override refraction strength per axis. |
depth | Controls how far refraction reaches from the edge toward the center. |
curvature | Adds the convex magnification through the middle of the lens. |
dispersion | Controls chromatic separation around refracted edges. |
bend | Adds the stronger liquid-style refraction near the rim. |
bendWidth | Sets the thickness of the rim refraction band. |
splay | Pushes the displacement field outward near corners. |
frost | Adds blur to the refracted source. |
brightness | Applies a white or black veil over the lens. |
saturate | Adjusts material-mode color saturation. |
specular | Sets the shared intensity for highlights and glow. |
sheen | Controls directional edge highlights. |
sheenWidth | Sets the thickness of the highlight band. |
sheenFalloff | Adjusts highlight falloff. |
sheenAngle | Changes the highlight direction in degrees. |
sheenDark | Uses a darker edge treatment in DOM mode. |
glow | Controls inner glow intensity. |
glowSpread | Sets how far the inner glow reaches. |
glowFalloff | Adjusts glow falloff. |
mapSize | Sets the displacement-map resolution. |
clipToShape | Limits optical effects to the rounded lens shape. |
softEdge | Adds a softer transition toward the lens edge. |
edgeShadow | Adds a shadow for the active lens state. |
edgeInsetShadow | Adds an inset shadow for the active lens state. |
restEdgeShadow | Adds a shadow for the resting lens state. |
restEdgeInsetShadow | Adds an inset shadow for the restinglens state. |
The same optics vocabulary drives both SVG-based DOM refraction and WebGL media surfaces.
Motion utilities
The package exports glassValue, animateGlassValue, deriveGlass, cubicBezier, glassEase, useLensWobble, rubberBand, and GlassDiv.
Use glassValue() for cursor-driven position changes, slider motion, or animated lens geometry. Use animateGlassValue() for timed transitions. deriveGlass() creates a value from other reactive lens values.
Alternatives and Related Resources
- Liquid Glass: WebGL2 Refraction Lens for Next.js and React
- Draggable Liquid Glass Lens Component for React and Next.js
- 40+ Apple Liquid Glass-inspired UI Components for Next.js
- Glassmorphism UI Component Library with TailwindCSS
- metal-fx: React WebGL Liquid Metal Effect
- Native iOS Liquid Glass Effects for React Native – expo-liquid-glass-view
FAQs
Q: Does liquid-glass work in Next.js App Router projects?
A: Yes. Render it inside a Client Component with "use client". Keep static page content in Server Components and isolate interactive glass sections behind a small client boundary.
Q: Why does the glass panel look different in Safari or Firefox?
A: A bare material wrapper frosts and tints correctly across browsers, but arbitrary backdrop refraction depends on Chromium behavior. Use a geometry-based lens or pass a matching node through refract for visible cross-browser refraction.
Q: Why does the lens have a warped or dark edge over an image?
A: Set behind to a matching background color. This fills the small area sampled outside a copied refraction target.
Q: Does the package work with video?
A: Yes. Pass a video URL through src, then use videoRef, paused, and lenses for custom transport controls. Remote media needs an appropriate crossOrigin setting and server-side CORS headers.
Q: Is a CSS import required?
A: No. Style the wrapper with inline styles, CSS classes, CSS modules, or Tailwind utilities.




