Loaders
Shape Trace Loader
A trio of SVG loaders (circle, triangle, square) that each trace their own outline with animated stroke-dashoffset, while a small dot orbits around them. No dependency needed.
Loading preview
Advertisement
Ad space reserved for a future placement
Source code
"use client";
interface ShapeTraceLoaderProps {
pathColor?: string;
dotColor?: string;
duration?: string;
label?: string;
className?: string;
}
export function ShapeTraceLoader({
pathColor = "#2f3545",
dotColor = "#5628ee",
duration = "3s",
label = "Loading",
className,
}: ShapeTraceLoaderProps) {
return (
<div
role="status"
aria-label={label}
className={["shape-trace-loader-group", className].filter(Boolean).join(" ")}
style={{
["--path" as string]: pathColor,
["--dot" as string]: dotColor,
["--duration" as string]: duration,
}}
>
<div className="loader">
<svg viewBox="0 0 80 80">
<rect height={64} width={64} y={8} x={8} />
</svg>
</div>
<div className="loader triangle">
<svg viewBox="0 0 86 80">
<polygon points="43 8 79 72 7 72" />
</svg>
</div>
<div className="loader">
<svg viewBox="0 0 80 80">
<circle r={32} cy={40} cx={40} />
</svg>
</div>
<style jsx>{`
.shape-trace-loader-group {
display: inline-flex;
}
.loader {
width: 44px;
height: 44px;
position: relative;
display: inline-block;
margin: 0 16px;
}
.loader:before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
position: absolute;
display: block;
background: var(--dot);
top: 37px;
left: 19px;
transform: translate(-18px, -18px);
animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg {
display: block;
width: 100%;
height: 100%;
}
.loader svg rect,
.loader svg polygon,
.loader svg circle {
fill: none;
stroke: var(--path);
stroke-width: 10px;
stroke-linejoin: round;
stroke-linecap: round;
}
.loader svg polygon {
stroke-dasharray: 145 76 145 76;
stroke-dashoffset: 0;
animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg rect {
stroke-dasharray: 192 64 192 64;
stroke-dashoffset: 0;
animation: pathRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg circle {
stroke-dasharray: 150 50 150 50;
stroke-dashoffset: 75;
animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader.triangle {
width: 48px;
}
.loader.triangle:before {
left: 21px;
transform: translate(-10px, -18px);
animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
@keyframes pathTriangle {
33% {
stroke-dashoffset: 74;
}
66% {
stroke-dashoffset: 147;
}
100% {
stroke-dashoffset: 221;
}
}
@keyframes dotTriangle {
33% {
transform: translate(0, 0);
}
66% {
transform: translate(10px, -18px);
}
100% {
transform: translate(-10px, -18px);
}
}
@keyframes pathRect {
25% {
stroke-dashoffset: 64;
}
50% {
stroke-dashoffset: 128;
}
75% {
stroke-dashoffset: 192;
}
100% {
stroke-dashoffset: 256;
}
}
@keyframes dotRect {
25% {
transform: translate(0, 0);
}
50% {
transform: translate(18px, -18px);
}
75% {
transform: translate(0, -36px);
}
100% {
transform: translate(-18px, -18px);
}
}
@keyframes pathCircle {
25% {
stroke-dashoffset: 125;
}
50% {
stroke-dashoffset: 175;
}
75% {
stroke-dashoffset: 225;
}
100% {
stroke-dashoffset: 275;
}
}
@media (prefers-reduced-motion: reduce) {
.loader:before,
.loader svg polygon,
.loader svg rect,
.loader svg circle {
animation-duration: 6s;
}
}
`}</style>
</div>
);
}
Usage
Import the component and use it like this.
import { ShapeTraceLoader } from "./ShapeTraceLoader";
export default function Example() {
return <ShapeTraceLoader pathColor="#2f3545" dotColor="#5628ee" duration="3s" label="Loading" />;
}
Dependencies
No external dependencies.
Usage notes
Pass pathColor (outline), dotColor (orbiting dot), duration (CSS time string), and an optional label.