# Statistics 1

> Statistics 1 variant for block.

A shadcn/ui block from the [@uiable](https://uiable.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiable/block-statistics-1).

- Page, with a live preview: https://shadcn.exchange/r/uiable/block-statistics-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiable/block-statistics-1-light-a986139227c8cf15.png
- Registry item: https://uiable.com/r/block-statistics-1.json
- Categories: statistics
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiable.com/r/block-statistics-1.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `framer-motion`, `lucide-react`
- Registry items: `@uiable/button`

## Files

### `src/components/uiable/blocks/statistics/statistics-1/statistics-1.tsx`

```tsx
"use client"

import { useEffect, useRef, useState } from "react"

// shadcn
import { Button } from "@/components/ui/button"

// third-party
import { animate, motion, useInView } from "framer-motion"

// assets
import { ArrowRight, Briefcase, Globe, Heart, Users } from "lucide-react"

const steps = [
  {
    value: 360,
    suffix: "+",
    description: "Well Trained Staff",
    colorClass: "text-pink-500",
    icon: Users,
  },
  {
    value: 14,
    suffix: "+",
    description: "Offices Worldwide",
    colorClass: "text-cyan-500",
    icon: Globe,
  },
  {
    value: 1440,
    suffix: "+",
    description: "Completed Projects",
    colorClass: "text-lime-500",
    icon: Briefcase,
  },
  {
    value: 56,
    suffix: "+",
    description: "Loyal Clients",
    colorClass: "text-amber-500",
    icon: Heart,
  },
]

function CountUp({ value, suffix }: { value: number; suffix?: string }) {
  const ref = useRef<HTMLSpanElement>(null)
  const isInView = useInView(ref, { once: true, margin: "-100px" })
  const [display, setDisplay] = useState(0)

  useEffect(() => {
    if (!isInView) return

    const controls = animate(0, value, {
      duration: 2,
      ease: "easeOut",
      onUpdate: (latest) => setDisplay(Math.floor(latest)),
    })

    return () => controls.stop()
  }, [isInView, value])

  return (
    <span ref={ref}>
      {display.toLocaleString()}
      {suffix}
    </span>
  )
}

//  ------------------------------ | STATISTICS 1 | ------------------------------  //

export default function Statistics1() {
  return (
    <section className="relative overflow-hidden bg-slate-900 py-24 sm:py-32">
      {/* background gradient effects */}
      <div className="pointer-events-none absolute inset-0 z-0 bg-gradient-to-b from-slate-950 via-slate-900 to-slate-950" />
      <div className="pointer-events-none absolute -top-24 left-1/4 z-0 size-96 -translate-x-1/2 rounded-full bg-blue-500/20 blur-3xl" />
      <div className="pointer-events-none absolute right-1/4 -bottom-24 z-0 size-96 translate-x-1/2 rounded-full bg-cyan-500/10 blur-3xl" />
      <div className="pointer-events-none absolute inset-0 z-0 bg-[radial-gradient(ellipse_at_center,_transparent_40%,_rgba(2,6,23,0.6)_100%)]" />

      <div className="relative z-30 container mx-auto px-4 sm:px-6 lg:px-8">
        <div className="flex flex-col items-center gap-10 sm:gap-14">
          <div className="flex flex-col items-center gap-4 text-center sm:gap-6">
            <div className="flex flex-col items-center gap-1 text-center">
              <motion.span
                className="text-sm font-medium tracking-wider text-slate-500 uppercase"
                initial={{ opacity: 0, y: 12 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true, margin: "-100px" }}
                transition={{ duration: 0.5 }}
              >
                Statistics
              </motion.span>
              <motion.h2
                className="text-lg font-medium text-slate-100 sm:text-3xl"
                initial={{ opacity: 0, y: 12 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true, margin: "-100px" }}
                transition={{ duration: 0.5, delay: 0.1 }}
              >
                What Numbers Say
              </motion.h2>
            </div>
            <motion.p
              className="max-w-150 text-slate-300"
              initial={{ opacity: 0, y: 12 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true, margin: "-100px" }}
              transition={{ duration: 0.5, delay: 0.2 }}
            >
              Discover how simple it is to integrate, configure, monitor, and
              scale your business operations with our next-generation SaaS
              platform.
            </motion.p>
          </div>
          <div className="relative mx-auto grid w-full max-w-5xl grid-cols-2 gap-y-6 overflow-hidden sm:gap-y-8 lg:grid-cols-4 lg:gap-y-0">
            {steps.map((step, idx) => (
              <motion.div
                key={idx}
                initial={{ opacity: 0, y: 20 }}
                whileInView={{ opacity: 1, y: 0 }}
                viewport={{ once: true, margin: "-100px" }}
                transition={{ duration: 0.6, delay: idx * 0.15 }}
              >
                <div className="group relative rounded-lg p-6 xl:p-8">
                  <div className="flex flex-col items-center gap-4 md:gap-6">
                    <step.icon className="size-6 stroke-2 text-slate-500 transition duration-300 group-hover:scale-125 group-hover:text-slate-300 md:size-8" />
                    <div className="flex flex-col items-center gap-4 xl:gap-6">
                      <h2
                        className={`${step.colorClass} text-lg font-medium sm:text-4xl`}
                      >
                        <CountUp value={step.value} suffix={step.suffix} />
                      </h2>
                      <p className="text-center text-base font-semibold tracking-wider text-slate-100 uppercase">
                        {step.description}
                      </p>
                    </div>
                  </div>
                </div>
              </motion.div>
            ))}
          </div>
          {/* Primary fill — gradient slides in on hover (see hero-12) */}
          <motion.div
            initial="rest"
            whileHover="hover"
            whileTap="tap"
            className="relative overflow-hidden rounded-full"
          >
            <Button
              size="lg"
              className="relative overflow-hidden rounded-full border-0 border-b-2 border-b-blue-700 bg-blue-500 px-8 text-base text-white hover:bg-blue-500"
            >
              {/* Gradient sliding background */}
              <motion.span
                className="absolute inset-0 z-0 rounded-full bg-gradient-to-r from-blue-500 to-cyan-500"
                variants={{
                  rest: { scaleX: 0 },
                  hover: { scaleX: 1 },
                }}
                style={{ originX: 0 }}
                transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
              />

              {/* Button content */}
              <span className="relative z-10 flex items-center justify-center gap-2">
                <motion.span
                  variants={{ rest: { x: 4 }, hover: { x: 0 } }}
                  transition={{ duration: 0.4, ease: "easeOut" }}
                >
                  Get Started
                </motion.span>
                <motion.span
                  variants={{ rest: { x: 0 }, hover: { x: 8 } }}
                  transition={{ duration: 0.4, ease: "easeOut" }}
                >
                  <ArrowRight className="size-4 shrink-0" />
                </motion.span>
              </span>
            </Button>
          </motion.div>
        </div>
      </div>
    </section>
  )
}
```
