shadcnui-blocks
blockhero-06source: ok · refreshed
Hero 06
A simple hero block
npx shadcn add "https://shadcnui-blocks.com/r/hero-06.json"free
Where it goes
hero
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Registry dependencies
badgebuttonhttps://www.shadcnui-blocks.com/r/dot-pattern.jsonhttps://www.shadcnui-blocks.com/r/particles.json
Files4 · source cached 10/8/2026, 3:26:21 AM
src/registry/blocks/radix/hero-06/components/hero.tsx
import { ArrowUpRight, CirclePlay } from "lucide-react";
import Link from "next/link";
import { BackgroundPattern } from "@/registry/blocks/radix/hero-06/components/background-pattern";
import { Badge } from "@/registry/bases/radix/ui/badge";
import { Button } from "@/registry/bases/radix/ui/button";
export default function Hero() {
return (
<div className="flex min-h-screen items-center justify-center px-6">
<BackgroundPattern />
<div className="relative z-10 max-w-3xl text-center">
<Badge
asChild
className="rounded-full border-border py-1"
variant="secondary"
>
<Link href="#">
Just released v1.0.0 <ArrowUpRight className="ml-1 size-4" />
</Link>
</Badge>
<h1 className="mx-auto mt-6 max-w-xl font-medium text-4xl tracking-tight sm:text-[2.75rem] md:text-6xl/[1.2]">
Ship better UI without the hassle
</h1>
<p className="mx-auto mt-6 max-w-2xl text-muted-foreground text-xl md:text-2xl/normal">
Instead of starting from scratch every time, use thoughtfully designed
blocks that give you a solid foundation for any UI.
</p>
<div className="mt-12 flex items-center justify-center gap-4">
<Button className="rounded-full" size="lg">
Get Started <ArrowUpRight className="h-5! w-5!" />
</Button>
<Button
className="rounded-full shadow-none"
size="lg"
variant="outline"
>
<CirclePlay className="h-5! w-5!" /> Watch Demo
</Button>
</div>
</div>
</div>
);
}
src/registry/blocks/radix/hero-06/components/background-pattern.tsx
"use client";
import { useTheme } from "next-themes";
import DotPattern from "@/registry/bases/radix/ui/dot-pattern";
import Particles from "@/registry/bases/radix/ui/particles";
import { cn } from "@/lib/utils";
export const BackgroundPattern = () => {
const { resolvedTheme } = useTheme();
const isLightTheme = resolvedTheme === "light";
return (
<>
<DotPattern
className={cn(
"mask-[radial-gradient(ellipse,rgba(0,0,0,0.3)_30%,black_50%)]",
"dark:fill-slate-700"
)}
cr={1}
cx={1}
cy={1}
height={20}
width={20}
/>
<Particles
className="absolute inset-0"
color={isLightTheme ? "#000" : "#fff"}
ease={80}
quantity={100}
refresh
/>
</>
);
};
src/registry/blocks/radix/hero-06/components/ui/dot-pattern.tsx
import { type ComponentProps, useId } from "react";
import { cn } from "@/lib/utils";
interface DotPatternProps extends ComponentProps<"svg"> {
width?: number;
height?: number;
x?: number;
y?: number;
cx?: number;
cy?: number;
cr?: number;
className?: string;
}
export function DotPattern({
width = 16,
height = 16,
x = 0,
y = 0,
cx = 1,
cy = 1,
cr = 1,
className,
...props
}: DotPatternProps) {
const id = useId();
return (
<svg
aria-hidden="true"
className={cn(
"pointer-events-none absolute inset-0 h-full w-full fill-neutral-400/80",
className
)}
{...props}
>
<defs>
<pattern
height={height}
id={id}
patternContentUnits="userSpaceOnUse"
patternUnits="userSpaceOnUse"
width={width}
x={x}
y={y}
>
<circle cx={cx} cy={cy} id="pattern-circle" r={cr} />
</pattern>
</defs>
<rect fill={`url(#${id})`} height="100%" strokeWidth={0} width="100%" />
</svg>
);
}
export default DotPattern;
src/registry/blocks/radix/hero-06/components/ui/particles.tsx
"use client";
import type React from "react";
import { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
interface MousePosition {
x: number;
y: number;
}
function MousePosition(): MousePosition {
const [mousePosition, setMousePosition] = useState<MousePosition>({
x: 0,
y: 0,
});
useEffect(() => {
const handleMouseMove = (event: MouseEvent) => {
setMousePosition({ x: event.clientX, y: event.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
return mousePosition;
}
interface ParticlesProps {
className?: string;
quantity?: number;
staticity?: number;
ease?: number;
size?: number;
refresh?: boolean;
color?: string;
vx?: number;
vy?: number;
}
function hexToRgb(hex: string): number[] {
hex = hex.replace("#", "");
if (hex.length === 3) {
hex = hex
.split("")
.map((char) => char + char)
.join("");
}
const hexInt = Number.parseInt(hex, 16);
const red = (hexInt >> 16) & 255;
const green = (hexInt >> 8) & 255;
const blue = hexInt & 255;
return [red, green, blue];
}
const Particles: React.FC<ParticlesProps> = ({
className = "",
quantity = 100,
staticity = 50,
ease = 50,
size = 0.4,
refresh = false,
color = "#ffffff",
vx = 0,
vy = 0,
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const canvasContainerRef = useRef<HTMLDivElement>(null);
const context = useRef<CanvasRenderingContext2D | null>(null);
const circles = useRef<Circle[]>([]);
const mousePosition = MousePosition();
const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 });
const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1;
useEffect(() => {
if (canvasRef.current) {
context.current = canvasRef.current.getContext("2d");
}
initCanvas();
animate();
window.addEventListener("resize", initCanvas);
return () => {
window.removeEventListener("resize", initCanvas);
};
}, [color]);
useEffect(() => {
onMouseMove();
}, [mousePosition.x, mousePosition.y]);
useEffect(() => {
initCanvas();
}, [refresh]);
const initCanvas = () => {
resizeCanvas();
drawParticles();
};
const onMouseMove = () => {
if (canvasRef.current) {
const rect = canvasRef.current.getBoundingClientRect();
const { w, h } = canvasSize.current;
const x = mousePosition.x - rect.left - w / 2;
const y = mousePosition.y - rect.top - h / 2;
const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2;
if (inside) {
mouse.current.x = x;
mouse.current.y = y;
}
}
};
type Circle = {
x: number;
y: number;
translateX: number;
translateY: number;
size: number;
alpha: number;
targetAlpha: number;
dx: number;
dy: number;
magnetism: number;
};
const resizeCanvas = () => {
if (canvasContainerRef.current && canvasRef.current && context.current) {
circles.current.length = 0;
canvasSize.current.w = canvasContainerRef.current.offsetWidth;
canvasSize.current.h = canvasContainerRef.current.offsetHeight;
canvasRef.current.width = canvasSize.current.w * dpr;
canvasRef.current.height = canvasSize.current.h * dpr;
canvasRef.current.style.width = `${canvasSize.current.w}px`;
canvasRef.current.style.height = `${canvasSize.current.h}px`;
context.current.scale(dpr, dpr);
}
};
const circleParams = (): Circle => {
const x = Math.floor(Math.random() * canvasSize.current.w);
const y = Math.floor(Math.random() * canvasSize.current.h);
const translateX = 0;
const translateY = 0;
const pSize = Math.floor(Math.random() * 2) + size;
const alpha = 0;
const targetAlpha = Number.parseFloat(
(Math.random() * 0.6 + 0.1).toFixed(1)
);
const dx = (Math.random() - 0.5) * 0.1;
const dy = (Math.random() - 0.5) * 0.1;
const magnetism = 0.1 + Math.random() * 4;
return {
x,
y,
translateX,
translateY,
size: pSize,
alpha,
targetAlpha,
dx,
dy,
magnetism,
};
};
const rgb = hexToRgb(color);
const drawCircle = (circle: Circle, update = false) => {
if (context.current) {
const { x, y, translateX, translateY, size, alpha } = circle;
context.current.translate(translateX, translateY);
context.current.beginPath();
context.current.arc(x, y, size, 0, 2 * Math.PI);
context.current.fillStyle = `rgba(${rgb.join(", ")}, ${alpha})`;
context.current.fill();
context.current.setTransform(dpr, 0, 0, dpr, 0, 0);
if (!update) {
circles.current.push(circle);
}
}
};
const clearContext = () => {
if (context.current) {
context.current.clearRect(
0,
0,
canvasSize.current.w,
canvasSize.current.h
);
}
};
const drawParticles = () => {
clearContext();
const particleCount = quantity;
for (let i = 0; i < particleCount; i++) {
const circle = circleParams();
drawCircle(circle);
}
};
const remapValue = (
value: number,
start1: number,
end1: number,
start2: number,
end2: number
): number => {
const remapped =
((value - start1) * (end2 - start2)) / (end1 - start1) + start2;
return remapped > 0 ? remapped : 0;
};
const animate = () => {
clearContext();
circles.current.forEach((circle: Circle, i: number) => {
// Handle the alpha value
const edge = [
circle.x + circle.translateX - circle.size, // distance from left edge
canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge
circle.y + circle.translateY - circle.size, // distance from top edge
canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
];
const closestEdge = edge.reduce((a, b) => Math.min(a, b));
const remapClosestEdge = Number.parseFloat(
remapValue(closestEdge, 0, 20, 0, 1).toFixed(2)
);
if (remapClosestEdge > 1) {
circle.alpha += 0.02;
if (circle.alpha > circle.targetAlpha) {
circle.alpha = circle.targetAlpha;
}
} else {
circle.alpha = circle.targetAlpha * remapClosestEdge;
}
circle.x += circle.dx + vx;
circle.y += circle.dy + vy;
circle.translateX +=
(mouse.current.x / (staticity / circle.magnetism) - circle.translateX) /
ease;
circle.translateY +=
(mouse.current.y / (staticity / circle.magnetism) - circle.translateY) /
ease;
drawCircle(circle, true);
// circle gets out of the canvas
if (
circle.x < -circle.size ||
circle.x > canvasSize.current.w + circle.size ||
circle.y < -circle.size ||
circle.y > canvasSize.current.h + circle.size
) {
// remove the circle from the array
circles.current.splice(i, 1);
// create a new circle
const newCircle = circleParams();
drawCircle(newCircle);
// update the circle position
}
});
window.requestAnimationFrame(animate);
};
return (
<div
aria-hidden="true"
className={cn("pointer-events-none", className)}
ref={canvasContainerRef}
>
<canvas className="size-full" ref={canvasRef} />
</div>
);
};
export default Particles;
More from shadcnui-blocks
Default AccordioncomponentOutline AccordioncomponentBox AccordioncomponentContained AccordioncomponentBox Contained AccordioncomponentTabs AccordioncomponentHighlight Active Item AccordioncomponentMultiple Expanded at a time Accordioncomponent
Browse all components in this library →