All-Access · 23 templates$1319.97$99/year — save 92%Get access
  • HomeHome
  • TemplatesTemplates17+
  • ComponentsComponents80+
  • PricingPricing
  • Need a Website?Need a Website?New
Unlock All AccessUnlock All Access

Our latest template collection

Ready-to-launch sites for real estate agents, service businesses, and local brands — built to rank, load fast, and convert.

Explore All TemplatesExplore All Templates

Orvaine - Luxury Equestrian & Horse Breeding Website Template

Orvaine is a premium website template for stud farms, sport horse breeders and equestrian estates: horse pages with pedigrees, events, films. Next.js, Astro, Sanity.

Download Template
Orvaine - Luxury Equestrian & Horse Breeding Website Template

Maressa - Luxury Yacht Charter & Sales Website Template

Maressa is a premium website template for yacht charter, brokerage and superyacht brands: 21 editable sections, full-screen films, Next.js and Astro versions. Sanity.

Download Template
Maressa - Luxury Yacht Charter & Sales Website Template

Krishi – Agriculture & Farm Website Template

Krishi is a premium website template for farms, agritech and food brands: 17 editable sections, cinematic GSAP motion and both Next.js and Astro versions. Built with Sanity.

Download Template
Krishi – Agriculture & Farm Website Template
ComponentsFeaturesCount-Up Stats
FreeReactTailwind CSSNext.js

Count-Up Stats

A dark stats band whose numbers count up when scrolled into view, with prefixes and suffixes. React and Tailwind CSS, no animation library.

View codeView code
Full screen
CountUpStats.tsx
"use client";

// A row of large numbers that count up when they scroll into view, on a dark band.
// Pure Tailwind + React, no animation library. Works with Tailwind v3 and v4.

import { useEffect, useRef } from "react";

type Stat = { value: number; label: string; prefix?: string; suffix?: string };

type Props = {
  heading?: string;
  stats?: Stat[];
};

const DEFAULT_STATS: Stat[] = [
  { value: 28, label: "Summers on the Mediterranean" },
  { value: 1400, suffix: "+", label: "Charters arranged for private clients" },
  { value: 96, suffix: "%", label: "Of guests return or recommend us" },
  { value: 3.2, prefix: "€", suffix: "bn", label: "In yacht sales brokered" },
];

const format = (n: number, decimals: number) =>
  n.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });

const decimalsOf = (value: number) => (Number.isInteger(value) ? 0 : String(value).split(".")[1].length);

// Renders the final value on the server; counts from zero once visible
function Counter({ value }: { value: number }) {
  const ref = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    const decimals = decimalsOf(value);
    const el = ref.current;
    if (!el || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    let frame = 0;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (!entry.isIntersecting) return;
        observer.disconnect();
        const start = performance.now();
        const tick = (now: number) => {
          const t = Math.min((now - start) / 2200, 1);
          const eased = t === 1 ? 1 : 1 - Math.pow(2, -10 * t); // expo out
          el.textContent = format(value * eased, decimals);
          if (t < 1) frame = requestAnimationFrame(tick);
        };
        frame = requestAnimationFrame(tick);
      },
      { rootMargin: "0px 0px -10% 0px" },
    );
    observer.observe(el);
    return () => {
      observer.disconnect();
      cancelAnimationFrame(frame);
    };
  }, [value]);

  return <span ref={ref}>{format(value, decimalsOf(value))}</span>;
}

export default function CountUpStats({
  heading = "Three decades on the water",
  stats = DEFAULT_STATS,
}: Props) {
  return (
    <section className="bg-[#0b131a] py-24 font-['Helvetica_Neue',Helvetica,Arial,sans-serif] text-white antialiased md:py-32">
      <div className="mx-auto max-w-[100rem] px-4 md:px-8 lg:px-12">
        {heading && (
          <h2 className="mb-14 text-[clamp(1.875rem,3.3vw,3.5rem)] font-normal leading-[1.05] tracking-[-0.03em] md:mb-20 md:max-w-3xl">
            {heading}
          </h2>
        )}
        <div className="grid grid-cols-2 gap-x-6 gap-y-14 lg:grid-cols-4">
          {stats.map((s) => (
            <div key={s.label} className="border-t border-white/15 pt-8">
              <p className="text-[clamp(2.25rem,4.5vw,4.75rem)] leading-none tracking-[-0.035em] tabular-nums">
                {s.prefix}
                <Counter value={s.value} />
                {s.suffix && <span className="text-[#dcc7a0]">{s.suffix}</span>}
              </p>
              <p className="mt-4 max-w-56 text-sm leading-relaxed text-white/60">{s.label}</p>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

How to use it

  1. Make sure your Next.js or React project has Tailwind CSS set up.
  2. Create components/CountUpStats.tsx and paste the code.
  3. Import it into a page and replace the placeholder text and images with your own.
Maressa - Luxury Yacht Charter & Sales Website Template
From the full template
Maressa - Luxury Yacht Charter & Sales Website Template$79

This section is one part of Maressa - Luxury Yacht Charter & Sales Website Template: a complete website with every page, Sanity CMS and SEO already set up.

Get the full template Get the full template Live preview Live preview

More free components

All features →
Expanding Image Panels

Expanding Image Panels

Numbered Process Steps

Numbered Process Steps

Image Trails

Image Trails

Ready for a faster site that's easier to edit?

Book a free 30-minute call. We'll review your current site and send a fixed-price plan. No pressure, no pitch.

Book Free Call Book Free Call No commitment required

Fast Next.js + Sanity websites, built for you or ready to launch with our templates.

Book a Free Call Book a Free Call
Services
  • Web Design
  • UI/UX Design
  • Framer Dev
  • Webflow Dev
  • Custom Dev
  • Sanity CMS
Resources
  • Templates
  • Pricing
  • License
  • Components
  • Case Studies
Company
  • About Us
  • Contact
  • Privacy Policy
© 2026 rayso.studio — All rights reservedDesigned & built in-house Remote-first agency
Let's TalkLet's Talk