shadcn.io is not affiliated with official shadcn/ui
Smoke Shader draws plumes of smoke on the GPU. Two soft grey columns rise from the bottom edge, with a third at the right, each widening as it climbs, swaying side to side and gone by about the middle of the box. Layers of fractal noise churn inside them, a faint haze drifts in the upper half, and a warm tint and a soft diagonal light catch the smoke near its base. Every pixel is computed each frame by a WGSL fragment shader on WebGPU, so the texture keeps rolling upward in a way CSS can't.
Use it behind a dark hero, an event or music page, or a section that should feel smoky and slow. The upper half stays dark and calm, so copy sits there. For the same idea in plain CSS, with puffs that follow your theme, use the Smoke background; for one thin thread of smoke that curls, Incense Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows a gradient of the same plumes in browsers without WebGPU.
Installation
Usage
import { SmokeShader } from "@/components/ui/smoke-shader";<SmokeShader className="flex min-h-[32rem] items-start justify-center pt-24">
<h1 className="text-5xl font-semibold text-white">Ship faster</h1>
</SmokeShader>Smoke Shader is a container: your content sits on top of the smoke, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The backdrop is near-black in both color schemes, so use white or light text, and keep it in the upper half, where the smoke has thinned.
Demos
On a light page
backgroundColor sets the backdrop and smokeColor the smoke; denser smoke shades darker from it. Here a dark grey on zinc gives charcoal smoke on a light page, and density={0.8} thins it so the plumes stay soft instead of sooty.
In a card
The plumes scale with their box. density={1.3} thickens them and height={1.2} lets them climb and spread further, so they fill a small, tall surface.
Performance
About 1.5 ms of GPU time a frame at 1280×720 on an M2 Max laptop; slower GPUs take several times longer. Each pixel skips the plumes it's outside of, so the dark upper corners cost little. The smoke is soft, so quality={0.5} renders a quarter of the pixels and looks nearly the same, on a slow GPU or a large screen. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu (about 50 KB gzipped) loads only when the first one mounts.
Browser support
The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a CSS gradient of the same plumes, in your smokeColor and backgroundColor, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.
Accessibility
The smoke is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame of the plumes and stops. Nothing flashes: the smoke drifts and rolls over seconds. White text reads above 13:1 everywhere in the upper half, and stays above 4.5:1 even over the brightest smoke at the bottom, so large text is safe anywhere and body text belongs up top.
FAQ
Is it real volumetric smoke?
No: it's 2D. Each plume is a cone that widens with height and fades toward a top that wobbles over time, filled with layers of value noise that scroll upward, plus a pseudo-3D noise made of three 2D slices. There's no fluid simulation or ray marching, which is why it's cheap, and the soft result reads as smoke.
How do I use it on a light page?
Set a light backgroundColor and a dark smokeColor, as in the demo above: backgroundColor="#f4f4f5" with smokeColor="#52525b" gives charcoal smoke on zinc. Lower density a little so the base of the plumes stays grey rather than black.
What do height, dissipation and turbulence do?
height sets how high the plumes reach and how wide they spread as they climb, from 0.3 (short stubs) to 1.5 (tall, broad columns). dissipation sets how fast they thin with height: 0 keeps them thick to the top of the cone, and 2 fades them out a third of the way up. turbulence sets how much the texture inside churns, from 0 (smooth bands) to 2 (rough and broken).
Can I change the smoke color?
Yes: smokeColor sets the smoke where it's thin, and the denser parts shade darker from it, with a warmer tint near the base. backgroundColor sets the backdrop as it shows at the bottom edge; it darkens a little toward the top. Both take any CSS color, var(--primary) included, and follow a theme switch.
Will it slow my page down?
It costs about 1.5 ms of GPU time a frame at 1280×720 on a laptop, and the CPU only sets a few numbers each frame. It stops when it's off screen. On a slow GPU or a large screen, quality={0.5} renders at half resolution, a quarter of the pixels, and the soft smoke looks the same.
API Reference
SmokeShader
data-slot="smoke-shader"
backgroundColor?string
The backdrop, at the bottom edge; the grade darkens it a little toward the top. Any CSS colour. @default "#050b14".
- Default
"#050b14"
density?number
How thick the smoke is, from 0 (none) to 2 (dense). @default 1.
- Default
1
dissipation?number
How fast the smoke thins as it rises, from 0 (it doesn't) to 2 (it's gone by mid-height). @default 1.
- Default
1
fallback?string
What shows until the first frame and in browsers without WebGPU: a CSS
background value (gradients, sizes, a colour) that looks like the
shader. It can use the colour props' --shader-<field> variables.
height?number
How high the plumes reach and how wide they spread, from 0.3 to 1.5. @default 1.
- Default
1
interactive?boolean
Whether u.pointer follows the pointer. @default false.
paused?boolean
Holds the current frame. @default false.
quality?number
Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.
smokeColor?string
The smoke's colour where it's thin; denser smoke shades darker from it. Any CSS colour. @default "#e6e6e6".
- Default
"#e6e6e6"
speed?number
Animation speed: 1 is normal, 0 holds the frame. @default 1.
time?number
A fixed moment, in seconds: draws it and doesn't animate.
turbulence?number
How much the texture inside the plumes churns, from 0 (smooth) to 2 (rough). @default 1.
- Default
1
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Forwarded to the part.
Related
- Pro


Incense Shader
Ink, smoke & fireOne thin thread of smoke that curls
- Pro


Warped Noise Shader
Flow & gradientsMarbled swirls that fold instead of rise


Nebula Shader
SpaceSoft clouds in space, free
- Pro


Sparks Drifting Shader
Ink, smoke & fireA fire with a column of smoke above it
- Pro


Ink Vortex Shader
Ink, smoke & fireSmoke-like ink rings, on a light ground


Valley Mist Shader
Water & landscapesMist drifting through hills, free