Vertical Scroll

An infinite marquee ticker designed for high-density information streams. It utilizes a duplicated data loop and linear Framer Motion transitions to create a seamless vertical crawl, finished with a CSS-mask gradient for an elegant "fade-out" effect at the boundaries.

framer-motionlucide-react

Trending Now

Next.js 15 Partial Prerendering
84 Comments
Tailwind v4 Alpha is wild
112 Comments
Framer Motion vs Motion One
45 Comments
Best Shadcn alternatives?
31 Comments
Next.js 15 Partial Prerendering
84 Comments
Tailwind v4 Alpha is wild
112 Comments
Framer Motion vs Motion One
45 Comments
Best Shadcn alternatives?
31 Comments

Installation

Install the required dependencies:

npm install framer-motion lucide-react
components/ui/vertical-scroll.tsx
"use client";
import { motion } from "framer-motion";
import { TrendingUp, MessageCircle } from "lucide-react";

const TRENDS = [
  { title: "Next.js 15 Partial Prerendering", comments: 84 },
  { title: "Tailwind v4 Alpha is wild", comments: 112 },
  { title: "Framer Motion vs Motion One", comments: 45 },
  { title: "Best Shadcn alternatives?", comments: 31 },
];

export function VerticalScroll({ trends = TRENDS }) {
  const duplicatedTrends = [...trends, ...trends];

  return (
    <div className="p-6 rounded-4xl border border-zinc-200 dark:border-zinc-800 bg-zinc-50/50 dark:bg-zinc-900/30 overflow-hidden">
      <div className="flex items-center gap-2 mb-6">
        <TrendingUp size={14} className="text-red-500" />
        <h4 className="text-[10px] font-black uppercase tracking-widest text-zinc-400">
          Trending Now
        </h4>
      </div>

      <div className="relative h-40 [mask-image:linear-gradient(to_bottom,transparent,black_20%,black_80%,transparent)]">
        <motion.div
          animate={{
            y: [0, -100 * (trends.length / duplicatedTrends.length) + "%"],
          }}
          transition={{
            duration: 12,
            repeat: Infinity,
            ease: "linear",
            repeatType: "loop",
          }}
          className="flex flex-col"
        >
          {duplicatedTrends.map((trend, i) => (
            <div
              key={i}
              className="flex flex-col gap-1 cursor-pointer group py-3"
            >
              <span className="text-sm font-bold dark:text-zinc-200 group-hover:text-emerald-500 transition-colors line-clamp-1">
                {trend.title}
              </span>
              <div className="flex items-center gap-2 text-[10px] font-black text-zinc-500 uppercase">
                <MessageCircle size={10} />
                {trend.comments} Comments
              </div>
            </div>
          ))}
        </motion.div>
      </div>
    </div>
  );
}