Night Sky
A dynamic night sky background with twinkling stars, customizable density, and content layered on top.
Stars Above You
1. Copy the component file
Create a new file called night-sky-background.jsx in
your reusable components folder (for example
/src/components/) and paste the following code
into it.
night-sky-background.jsx
import { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from "react";
import styles from "./night-sky-background.module.css";
const random = (n = 1) => {
return Math.random() * n;
};
const NightSkyBackground = (props) => {
const {
density = 1,
children,
className,
wrapperProps = {},
wrapperTagName = "div",
...rest
} = props;
const {
className: wrapperClassName = "",
...restWrapperProps
} = wrapperProps;
const Wrapper = wrapperTagName || "div";
const spaceColor = "rgb(0, 0, 0)";
const starsCount = useMemo(() => (
Math.min(Math.max(100, 1000 * density), 10000)
), [density]);
const containerRef = useRef();
const canvasRef = useRef(null);
const rafId = useRef(null);
const [mounted, setMounted] = useState(false);
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const ctx = useMemo(() => {
return canvasRef.current?.getContext("2d");
}, [canvasRef.current]);
const stars = useMemo(() => {
const stars = [];
for (let i = 0; i < starsCount; i++) {
stars.push({
x: random(width),
y: random(height),
radius: random() + 0.25,
color: (
random() < 0.8 ? [255, 255, 255] : (
[
Math.floor(random(255)),
Math.floor(random(255)),
Math.floor(random(255)),
]
)
),
twinkingRate: random() * 0.02,
opacity: random(),
shouldTwinkle: random() < 0.5,
});
}
return stars;
}, [starsCount, width, height]);
const render = useCallback(() => {
ctx.fillStyle = spaceColor;
ctx.fillRect(0, 0, width, height);
ctx.save();
for (const star of stars) {
let starOpacity = star.opacity;
if (star.shouldTwinkle) {
star.opacity += star.twinkingRate;
starOpacity = Math.cos(star.opacity);
}
ctx.beginPath();
ctx.ellipse(
star.x,
star.y,
star.radius,
star.radius,
0,
0,
360,
false
);
ctx.closePath();
ctx.fillStyle = `rgba(${[...star.color, starOpacity].join(", ")})`;
ctx.fill();
}
ctx.restore();
rafId.current = requestAnimationFrame(render);
}, [ctx, width, height, stars]);
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();
return () => {
cancelAnimationFrame(rafId.current);
};
}, [mounted, render]);
return (
<div
{...rest}
ref={containerRef}
className={[
className,
styles["night-sky-background"]
].join(" ")}
>
<canvas
aria-hidden={true}
width={width}
height={height}
ref={canvasRef}
/>
<Wrapper
{...restWrapperProps}
className={[
wrapperClassName,
styles["wrapper"]
].join(" ")}
>
{children}
</Wrapper>
</div>
);
};
export default NightSkyBackground; 2. Copy the CSS module file
In the same folder, create a file called night-sky-background.module.css and paste the following CSS.
night-sky-background.module.css
.night-sky-background {
position: relative;
canvas {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.wrapper {
position: relative;
z-index: 3;
}
} 3. Use the component
Now you can import and use the component anywhere in your project.
night-sky-background-preview.jsx
import NightSkyBackground from "@/components/backgrounds/night-sky-background/night-sky-background";
const NightSkyBackgroundPreview = () => {
return (
<NightSkyBackground
style={{
width: "100%",
height: "100%"
}}
wrapperProps={{
style: {
height: "100%",
display: "grid",
placeItems: "center",
color: "#fff",
}
}}
>
<h1>Stars Above You</h1>
</NightSkyBackground>
);
};
export default NightSkyBackgroundPreview; Props
Configure the component with the following props:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| density | number | No | 1 | Controls the number of stars rendered in the background. Min: 100, Max: 10000. Higher = denser sky. |
| children | ReactNode | Yes | — | Content displayed on top of the night sky background. |
| className | string | No | — | Additional CSS classes applied to the main container. |
| wrapperProps | React.HTMLAttributes<any> | No | — | Extra props passed to the wrapper element containing the children. |
| wrapperTagName | string | No | "div" | HTML tag used as the wrapper element around the children. |