Dot Grid Gradient
A customizable dot grid with gradient scaling, allowing control over dot size, color, and shrink direction.
1. Copy the component file
Create a new file called dot-grid-gradient-background.jsx in
your reusable components folder (for example
/src/components/) and paste the following code
into it.
dot-grid-gradient-background.jsx
import { memo, useState, useRef, useMemo, useCallback, useEffect, useLayoutEffect } from "react";
import styles from "./dot-grid-gradient-background.module.css";
const DotGridGradientBackground = (props) => {
const {
children,
backgroundColor = "rgba(0, 0, 0, 1)",
dotColor = "rgba(250, 250, 250, 1)",
dotScale = 0.5,
direction = "down",
decay = 0.5,
className = "",
style = null,
wrapperTagName = "div",
wrapperProps = {},
...restProps
} = props;
const {
className: wrapperClassName = "",
...restWrapperProps
} = wrapperProps;
const Wrapper = wrapperTagName || "div";
const containerRef = useRef(null);
const canvasRef = useRef(null);
const [mounted, setMounted] = useState(false);
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const { devicePixelRatio, canvasWidth, canvasHeight } = useMemo(() => {
const devicePixelRatio = globalThis.devicePixelRatio || 1;
return {
devicePixelRatio,
canvasWidth: width * devicePixelRatio,
canvasHeight: height * devicePixelRatio,
};
}, [width, height]);
const ctx = useMemo(() => {
return canvasRef.current?.getContext("2d");
}, [canvasRef.current]);
const render = useCallback(() => {
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
const dotSize = 5 + (10 * dotScale);
const k = (1 + (decay * 15)) / (height / dotSize);
const offset = dotSize / 2;
const drawDot = (x, y, i, minDotRadius = 0.5) => {
const posX = x + offset;
const posY = y + offset;
const radius = (dotSize / 1.85) - (i * k);
ctx.beginPath();
ctx.arc(posX, posY, Math.max(minDotRadius, radius), 0, Math.PI * 2);
ctx.fillStyle = dotColor;
ctx.fill();
ctx.closePath();
};
if (["up", "down"].includes(direction)) {
for (let x = 0; x <= width; x += dotSize) {
if (direction === "up") {
for (let y = height; y >= -dotSize; y -= dotSize) {
drawDot(x, y, (height / dotSize) - (y / dotSize));
}
} else {
for (let y = 0; y <= height; y += dotSize) {
drawDot(x, y, y / dotSize);
}
}
}
} else if (["left", "right"].includes(direction)) {
for (let y = 0; y <= height; y += dotSize) {
if (direction === "left") {
for (let x = width; x >= -dotSize; x -= dotSize) {
drawDot(x, y, (width / dotSize) - (x / dotSize));
}
} else {
for (let x = 0; x <= width; x += dotSize) {
drawDot(x, y, x / dotSize);
}
}
}
}
}, [
ctx,
devicePixelRatio,
width,
height,
canvasWidth,
canvasHeight,
backgroundColor,
dotScale,
dotColor,
direction,
decay,
]);
useEffect(() => {
const updateContainerDimensions = () => {
const {
width,
height,
} = containerRef.current.getBoundingClientRect();
setWidth(width);
setHeight(height);
};
const resizeObserver = new ResizeObserver(updateContainerDimensions);
resizeObserver.observe(containerRef.current);
updateContainerDimensions();
setMounted(true);
return () => {
resizeObserver.disconnect();
};
}, []);
useLayoutEffect(() => {
if (!mounted) return;
render();
}, [mounted, render]);
return (
<div
{...restProps}
ref={containerRef}
className={[
className,
styles["dot-grid-gradient-background"],
].join(" ")}
style={{
...style,
"--background-color": backgroundColor,
}}
>
<canvas
aria-hidden={true}
width={canvasWidth}
height={canvasHeight}
ref={canvasRef}
/>
<Wrapper
{...restWrapperProps}
className={[
wrapperClassName,
styles["wrapper"],
].join(" ")}
>
{children}
</Wrapper>
</div>
);
};
export default memo(DotGridGradientBackground); 2. Copy the CSS module file
In the same folder, create a file called dot-grid-gradient-background.module.css and paste the following CSS.
dot-grid-gradient-background.module.css
.dot-grid-gradient-background {
position: relative;
background-color: var(--background-color);
>canvas {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 1;
}
>.wrapper {
position: relative;
z-index: 2;
}
} 3. Use the component
Now you can import and use the component anywhere in your project.
dot-grid-gradient-background-preview.jsx
import DotGridGradientBackground from "@/components/backgrounds/dot-grid-gradient-background/dot-grid-gradient-background";
const DotGridGradientBackgroundPreview = () => {
return (
<DotGridGradientBackground
dotColor="#bbb"
style={{
width: "100%",
height: "100%",
}}
wrapperProps = {{
style: {
display: "grid",
placeItems: "center",
height: "100%"
}
}}
>
<h2
style={{
color: "#fff"
}}>
Dot Grid Background
</h2>
</DotGridGradientBackground>
)
};
export default DotGridGradientBackgroundPreview; Direction
dot-grid-gradient-background
import { useState } from "react";
import DotGridGradientBackground from "@/components/backgrounds/dot-grid-gradient-background/dot-grid-gradient-background";
const DotGridGradientBackgroundDirectionPreview = () => {
const directions = ["up", "down", "left", "right"];
const [direction, setDirection] = useState("down");
return (
<DotGridGradientBackground
direction={direction}
style={dotGridBackgroundStyles}
wrapperProps = {{
style: wrapperStyles
}}
>
{directions.map(dir => (
<button
key={dir}
onClick={() => setDirection(dir)}
style={{
...buttomStyles,
...(dir === direction ? activeButtonStyles : null),
}}
>
{dir}
</button>
))}
</DotGridGradientBackground>
)
};
const dotGridBackgroundStyles = {
width: "100%",
height: "100%",
padding: "24px",
display: "flex",
justifyContent: "center",
alignItems: "center",
};
const wrapperStyles = {
display: "flex",
flexWrap: "wrap",
justifyContent: "center",
alignItems: "center",
gap: "16px",
};
const buttomStyles = {
padding: "8px 16px",
border: "1px solid #fff",
borderRadius: "4px",
background: "transparent",
font: "inherit",
color: "#fff",
textTransform: "uppercase",
};
const activeButtonStyles = {
background: "#fff",
color: "#111",
};
export default DotGridGradientBackgroundDirectionPreview; Dot Scale
dot-grid-gradient-background
import { useState } from "react";
import DotGridGradientBackground from "@/components/backgrounds/dot-grid-gradient-background/dot-grid-gradient-background";
const DotGridGradientBackgroundDotScalePreview = () => {
const [dotScale, setDotScale] = useState(0.5);
const handleDotScaleChange = (e) => {
setDotScale(parseFloat(e.target.value));
};
return (
<DotGridGradientBackground
dotScale={dotScale}
style={dotGridBackgroundStyles}
wrapperProps = {{
style: wrapperStyles,
}}
>
<span style={labelStyles}>
Dot Scale: {dotScale}
</span>
<input
type="range"
min="0.1"
max="1"
step="0.1"
value={dotScale}
onChange={handleDotScaleChange}
/>
</DotGridGradientBackground>
)
};
const dotGridBackgroundStyles = {
width: "100%",
height: "100%",
display: "grid",
placeItems: "center",
};
const wrapperStyles = {
display: "grid",
placeItems: "center",
gap: "8px",
};
const labelStyles = {
padding: "2px",
background: "#111",
color: "#fff",
};
export default DotGridGradientBackgroundDotScalePreview; Decay
dot-grid-gradient-background
import { useState } from "react";
import DotGridGradientBackground from "@/components/backgrounds/dot-grid-gradient-background/dot-grid-gradient-background";
const DotGridGradientBackgroundDecayPreview = () => {
const [decay, setDecay] = useState(0.5);
const handleDecayChange = (e) => {
setDecay(parseFloat(e.target.value));
};
return (
<DotGridGradientBackground
decay={decay}
style={dotGridBackgroundStyles}
wrapperProps = {{
style: wrapperStyles,
}}
>
<span style={labelStyles}>
Decay: {decay}
</span>
<input
type="range"
min="0.1"
max="1"
step="0.1"
value={decay}
onChange={handleDecayChange}
/>
</DotGridGradientBackground>
)
};
const dotGridBackgroundStyles = {
width: "100%",
height: "100%",
display: "grid",
placeItems: "center",
};
const wrapperStyles = {
display: "grid",
placeItems: "center",
gap: "8px",
};
const labelStyles = {
padding: "2px",
background: "#111",
color: "#fff",
};
export default DotGridGradientBackgroundDecayPreview; Props
Configure the component with the following props:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| children | ReactNode | No | — | Content rendered inside the component wrapper. |
| backgroundColor | string | No | "rgba(0,0,0,1)" | Background color of the grid. Accepts any valid CSS color such as rgba() or hex (#000). |
| dotColor | string | No | "rgba(250,250,250,1)" | Color used to render the dots. Accepts rgba() or hex values. |
| dotScale | number | No | 0.5 | Base scale of the dots. Range: 0.1 – 1. Controls the overall dot size. |
| direction | "up" | "down" | "left" | "right" | No | "down" | Direction in which the gradient shrink effect is applied. |
| decay | number | No | 0.5 | Controls how quickly dot sizes decrease across the gradient. Range: 0.1 – 1. |
| className | string | No | "" | Additional CSS class names applied to the root element. |
| style | React.CSSProperties | No | null | Inline styles applied to the root element. |
| wrapperTagName | string | No | "div" | HTML tag used as the wrapper element around the children. |
| wrapperProps | object | No | {} | Additional props passed to the wrapper element containing the children. |