Docs
Hyperspace Hero

Hyperspace Hero

A stunning hyperspace animation hero section with animated star streaks and gradient text

EXPLORE

Installation

Copy and paste the following code into your project.

"use client";
 
import React, { useEffect, useRef, useState } from "react";
 
interface Star {
  x: number;
  y: number;
  z: number;
  px: number;
  py: number;
  size: number;
  color: string;
}
 
interface HyperspaceHeroProps {
  text?: string;
  textColor?: string;
  starCount?: number;
  speed?: number;
  className?: string;
}
 
export function HyperspaceHero({
  text = "30k",
  textColor = "linear-gradient(135deg, #8a2be2, #ff69b4)",
  starCount = 400,
  speed = 2,
  className = "",
}: HyperspaceHeroProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
  const starsRef = useRef<Star[]>([]);
  const animationRef = useRef<number>(0);
 
  const initStars = () => {
    if (!canvasRef.current) return;
 
    const width = canvasRef.current.width;
    const height = canvasRef.current.height;
    const stars: Star[] = [];
 
    for (let i = 0; i < starCount; i++) {
      const star: Star = {
        x: Math.random() * width - width / 2,
        y: Math.random() * height - height / 2,
        z: Math.random() * 1000,
        px: 0,
        py: 0,
        size: Math.random() * 1.5 + 0.5,
        color: `rgba(${180 + Math.floor(Math.random() * 75)}, ${180 + Math.floor(Math.random() * 75)}, ${220 + Math.floor(Math.random() * 35)}, ${0.6 + Math.random() * 0.4})`,
      };
      stars.push(star);
    }
 
    starsRef.current = stars;
  };
 
  const drawStars = () => {
    if (!canvasRef.current) return;
 
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
 
    const width = canvas.width;
    const height = canvas.height;
    const centerX = width / 2;
    const centerY = height / 2;
 
    ctx.fillStyle = "#000";
    ctx.fillRect(0, 0, width, height);
 
    starsRef.current.forEach((star) => {
      star.z -= speed;
 
      if (star.z <= 0) {
        star.z = 1000;
        star.x = Math.random() * width - centerX;
        star.y = Math.random() * height - centerY;
      }
 
      const factor = 200 / star.z;
      star.px = star.x * factor + centerX;
      star.py = star.y * factor + centerY;
 
      const size = Math.min(star.size * (400 / star.z), 5);
 
      const tailLength = Math.min(30 * (speed / 2), 30);
      const prevFactor = 200 / (star.z + speed * 2);
      const prevX = star.x * prevFactor + centerX;
      const prevY = star.y * prevFactor + centerY;
 
      const gradient = ctx.createLinearGradient(prevX, prevY, star.px, star.py);
      gradient.addColorStop(0, "transparent");
      gradient.addColorStop(1, star.color);
 
      ctx.beginPath();
      ctx.moveTo(prevX, prevY);
      ctx.lineTo(star.px, star.py);
      ctx.strokeStyle = gradient;
      ctx.lineWidth = size;
      ctx.stroke();
 
      ctx.beginPath();
      ctx.arc(star.px, star.py, size / 2, 0, Math.PI * 2);
      ctx.fillStyle = star.color;
      ctx.fill();
    });
  };
 
  const animate = () => {
    drawStars();
    animationRef.current = requestAnimationFrame(animate);
  };
 
  useEffect(() => {
    const handleResize = () => {
      if (canvasRef.current && canvasRef.current.parentElement) {
        const parent = canvasRef.current.parentElement;
        const width = parent.clientWidth;
        const height = parent.clientHeight;
 
        setDimensions({ width, height });
        canvasRef.current.width = width;
        canvasRef.current.height = height;
 
        initStars();
      }
    };
 
    handleResize();
    window.addEventListener("resize", handleResize);
 
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);
 
  useEffect(() => {
    if (dimensions.width > 0 && dimensions.height > 0) {
      initStars();
      animate();
 
      return () => {
        cancelAnimationFrame(animationRef.current);
      };
    }
  }, [dimensions]);
 
  return (
    <div className={`relative h-full w-full overflow-hidden ${className}`}>
      <canvas ref={canvasRef} className="absolute inset-0 h-full w-full" />
      <div className="absolute inset-0 flex items-center justify-center">
        <h1
          className="z-10 text-[7rem] font-bold leading-none"
          style={{
            backgroundImage: textColor,
            backgroundClip: "text",
            WebkitBackgroundClip: "text",
            color: "transparent",
            WebkitTextStroke: "2px rgba(255,255,255,0.1)",
          }}
        >
          {text}
        </h1>
      </div>
    </div>
  );
}

Update the import paths to match your project setup.

import HyperspaceHero from "@/components/ui/hyperspace-hero";

Props

PropTypeDescriptionDefault
textstringThe text to display in the center of the hero section"30k"
textColorstringCSS gradient or color for the text"linear-gradient(135deg, #8a2be2, #ff69b4)"
starCountnumberNumber of stars in the hyperspace animation400
speednumberAnimation speed of the stars2
classNamestringAdditional CSS classes to apply to the container""

Examples

Custom Text and Colors

<HyperspaceHero
  text="LAUNCH"
  textColor="linear-gradient(135deg, #00c6ff, #0072ff)"
  starCount={300}
  speed={1.5}
/>

Full-Screen Hero

<div className="h-screen w-full">
  <HyperspaceHero
    text="EXPLORE"
    textColor="linear-gradient(135deg, #f5515f, #9f041b)"
    starCount={500}
    speed={3}
  />
</div>