shadimage
ratneshc
componentstars-travelsource: ok · hit

Stars Travel

A motion-driven SVG background with an animated 'infinite travel' effect.

npx shadcn add "https://ratneshc.com/r/stars-travel.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · medium
animationmotion

Files1 · source cached 10/15/2026, 8:08:23 AM

src/registry/components/stars-travel/stars-travel.tsx
"use client";

import { motion } from "motion/react";

import { cn } from "@/lib/utils";

export type StarsTravelProps = {
  /** Children nodes to render within the StarsTravel component. */
  children?: React.ReactNode;
  /** Optional CSS class names for the container. */
  className?: string;
  /** SVG animation options. */
  svgOptions?: {
    /** Duration of the SVG animation in milliseconds. */
    duration?: number;
  };
};

export function StarsTravel({
  children,
  className,
  svgOptions,
}: StarsTravelProps) {
  return (
    <div className={cn("relative w-full overflow-hidden", className)}>
      <SVG svgOptions={svgOptions} />
      {children}
    </div>
  );
}

const pathVariants = {
  initial: { strokeDashoffset: 500, strokeDasharray: "0 800" },
  animate: {
    strokeDashoffset: 0,
    strokeDasharray: "20 800",
    opacity: [0, 2, 2, 0],
  },
};

export type SVGProps = {
  /** SVG animation options. */
  svgOptions?: {
    /** Duration of the SVG animation in milliseconds. */
    duration?: number;
  };
};

const SVG = ({ svgOptions }: SVGProps) => {
  const paths = [
    "M720 450 L1431.52 -15.0078",
    "M720 450 L1250.42 -214.196",
    "M720 450 L258.602 -263.871",
    "M720 450 L1567.23 381.48",
    "M720 450 L-16.396 874.524",
    "M720 450 L243.151 1153.64",
    "M720 450 L1527.54 715.212",
    "M720 450 L925.531 -374.777",
    "M720 450 L822.905 1293.75",
    "M720 450 L1125.77 1196.89",
    "M720 450 L-77.4307 744.286",
    "M720 450 L1307.21 1064.56",
    "M720 450 L626.514 -394.844",
    "M720 450 L1029.67 1241.58",
    "M720 450 L-121.821 332.369",
    "M720 450 L524.129 -377.124",
    "M720 450 L472.792 -363.258",
    "M720 450 L906.742 1279.23",
    "M720 450 L-46.165 81.9082",
    "M720 450 L512.156 1274.2",
    "M720 450 L1368.98 -98.9326",
    "M720 450 L1892.34 567.89",
    "M720 450 L-234.56 -145.23",
    "M720 450 L1654.78 -287.45",
    "M720 450 L345.89 1087.34",
    "M720 450 L-389.12 456.78",
    "M720 450 L1745.23 123.56",
    "M720 450 L189.45 -298.67",
    "M720 450 L1123.67 -156.89",
    "M720 450 L-178.34 1045.23",
    "M720 450 L1456.78 892.34",
    "M720 450 L567.89 -423.12",
  ];

  return (
    <motion.svg
      viewBox="0 0 1440 900"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      preserveAspectRatio="none"
      initial={{ opacity: 0 }}
      whileInView={{ opacity: 1 }}
      viewport={{
        once: false,
        amount: 0.15,
      }}
      transition={{ duration: 1, delay: 0.1 }}
      className="absolute inset-0 h-full w-full text-foreground"
    >
      {paths.map((path, idx) => (
        <motion.path
          d={path}
          stroke="currentColor"
          strokeWidth="1"
          strokeLinecap="round"
          variants={pathVariants}
          initial="initial"
          animate="animate"
          transition={{
            duration: svgOptions?.duration || 2,
            ease: "easeIn",
            repeat: Infinity,
            repeatType: "loop",
            delay: (idx * 0.15) % 3,
            repeatDelay: 0,
          }}
          key={`path-first-${idx}`}
        />
      ))}
    </motion.svg>
  );
};

More from ratneshc

Browse all components in this library →