# Review Stats Overview

> A summary panel with average rating, total count, and a bar chart by star level. Gives shoppers a quick read on overall product quality up front.

A shadcn/ui block from the [@shadcn-ui-blocks](https://www.shadcn-ui-blocks.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-ui-blocks/ecommerce-ecommerce-customer-reviews-review-stats-overview).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-ui-blocks/ecommerce-ecommerce-customer-reviews-review-stats-overview
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-ui-blocks/ecommerce-ecommerce-customer-reviews-review-stats-overview-light-cfa548935c171af2.png
- Registry item: https://www.shadcn-ui-blocks.com/r/ecommerce-ecommerce-customer-reviews-review-stats-overview.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.shadcn-ui-blocks.com/r/ecommerce-ecommerce-customer-reviews-review-stats-overview.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: `class-variance-authority`, `lucide-react`, `radix-ui`

## Files

### `components/blocks/ecommerce/ecommerce-customer-reviews/review-stats-overview.tsx`

```tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Badge } from "@/components/ui/badge";
import { Star, TrendingUp, Users, ThumbsUp, Award } from "lucide-react";

export default function ReviewStatsOverview() {
  const stats = {
    averageRating: 4.8,
    totalReviews: 2547,
    recommendationRate: 97,
    ratingDistribution: [
      { stars: 5, count: 1893, percentage: 74 },
      { stars: 4, count: 509, percentage: 20 },
      { stars: 3, count: 102, percentage: 4 },
      { stars: 2, count: 25, percentage: 1 },
      { stars: 1, count: 18, percentage: 1 },
    ],
  };

  const highlights = [
    {
      icon: ThumbsUp,
      title: "Customer Satisfaction",
      value: "97%",
      description: "Would recommend to friends",
      trend: "+2.3%",
    },
    {
      icon: Users,
      title: "Total Reviews",
      value: "2,547",
      description: "Verified customer reviews",
      trend: "+324 this month",
    },
    {
      icon: Award,
      title: "Quality Rating",
      value: "4.8/5",
      description: "Average product rating",
      trend: "+0.2 this quarter",
    },
    {
      icon: TrendingUp,
      title: "Growth Rate",
      value: "15%",
      description: "Monthly review increase",
      trend: "Consistent growth",
    },
  ];

  const recentReviews = [
    {
      name: "Maria Garcia",
      rating: 5,
      review:
        "Outstanding quality and fast delivery. Exceeded my expectations!",
      product: "Wireless Earbuds Pro",
      time: "2 hours ago",
    },
    {
      name: "Kevin Johnson",
      rating: 5,
      review:
        "Perfect product, works exactly as described. Great value for money.",
      product: "Smart Home Hub",
      time: "5 hours ago",
    },
    {
      name: "Lisa Chen",
      rating: 4,
      review:
        "Really good product overall. Minor shipping delay but worth the wait.",
      product: "Bluetooth Speaker",
      time: "1 day ago",
    },
  ];

  return (
    <div className="container mx-auto px-4 py-24 md:px-6 lg:py-32 2xl:max-w-[1400px]">
      <div className="mb-16 text-center">
        <h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
          Customer Review Analytics
        </h2>
        <p className="text-muted-foreground mx-auto max-w-2xl text-lg">
          Comprehensive insights from our customer feedback and reviews
        </p>
      </div>

      {/* Stats Overview */}
      <div className="mb-12 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
        {highlights.map((highlight, index) => (
          <Card key={index} className="gap-3">
            <CardHeader className="flex flex-row items-center justify-between space-y-0">
              <CardTitle className="text-sm font-medium">
                {highlight.title}
              </CardTitle>
              <highlight.icon className="text-muted-foreground size-4" />
            </CardHeader>
            <CardContent>
              <div className="text-2xl font-bold">{highlight.value}</div>
              <p className="text-muted-foreground mb-2 text-xs">
                {highlight.description}
              </p>
              <Badge variant="secondary" className="text-xs">
                {highlight.trend}
              </Badge>
            </CardContent>
          </Card>
        ))}
      </div>

      <div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
        {/* Rating Distribution */}
        <Card className="gap-3">
          <CardHeader>
            <CardTitle className="flex items-center gap-2">
              <Star className="size-5 fill-yellow-400 text-yellow-400" />
              Rating Distribution
            </CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-4">
              <div className="mb-6 flex items-center justify-between">
                <div className="">
                  <div className="text-3xl font-bold">
                    {stats.averageRating}
                  </div>
                  <div className="mt-1 flex items-center justify-center gap-1">
                    {[...Array(5)].map((_, i) => (
                      <Star
                        key={i}
                        className={`size-4 ${
                          i < Math.floor(stats.averageRating)
                            ? "fill-yellow-400 text-yellow-400"
                            : "text-muted-foreground"
                        }`}
                      />
                    ))}
                  </div>
                  <div className="text-muted-foreground mt-1 text-sm">
                    {stats.totalReviews.toLocaleString()} reviews
                  </div>
                </div>
              </div>

              {stats.ratingDistribution.map((rating) => (
                <div key={rating.stars} className="flex items-center gap-4">
                  <div className="flex w-16 items-center gap-1">
                    <span className="text-sm">{rating.stars}</span>
                    <Star className="size-3 fill-yellow-400 text-yellow-400" />
                  </div>
                  <Progress value={rating.percentage} className="h-2 flex-1" />
                  <div className="text-muted-foreground w-12 text-right text-sm">
                    {rating.count}
                  </div>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>

        {/* Recent Reviews */}
        <Card className="gap-3">
          <CardHeader>
            <CardTitle>Recent Reviews</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-6">
              {recentReviews.map((review, index) => (
                <div
                  key={index}
                  className="border-border border-b pb-4 last:border-0 last:pb-0"
                >
                  <div className="mb-2 flex items-start justify-between">
                    <div>
                      <h4 className="text-sm font-semibold">{review.name}</h4>
                      <div className="mt-1 flex items-center gap-1">
                        {[...Array(5)].map((_, i) => (
                          <Star
                            key={i}
                            className={`size-3 ${
                              i < review.rating
                                ? "fill-yellow-400 text-yellow-400"
                                : "text-muted-foreground"
                            }`}
                          />
                        ))}
                      </div>
                    </div>
                    <span className="text-muted-foreground text-xs">
                      {review.time}
                    </span>
                  </div>
                  <p className="text-muted-foreground mb-2 text-sm">
                    {review.review}
                  </p>
                  <p className="text-xs font-medium">{review.product}</p>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
      </div>

      {/* Bottom Stats */}
      <div className="mt-12 text-center">
        <Card className="inline-block">
          <CardContent>
            <div className="flex items-center gap-8">
              <div className="text-center">
                <div className="text-2xl font-bold text-green-600">
                  {stats.recommendationRate}%
                </div>
                <div className="text-muted-foreground text-sm">
                  Customers recommend us
                </div>
              </div>
              <div className="bg-border h-12 w-px" />
              <div className="text-center">
                <div className="text-2xl font-bold">4.8/5</div>
                <div className="text-muted-foreground text-sm">
                  Overall satisfaction
                </div>
              </div>
              <div className="bg-border h-12 w-px" />
              <div className="text-center">
                <div className="text-2xl font-bold">99.2%</div>
                <div className="text-muted-foreground text-sm">
                  Verified purchases
                </div>
              </div>
            </div>
          </CardContent>
        </Card>
      </div>
    </div>
  );
}
```

### `components/ui/card.tsx`

```tsx
import * as React from "react"

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

function Card({
  className,
  size = "default",
  ...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
  return (
    <div
      data-slot="card"
      data-size={size}
      className={cn(
        "group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
        className
      )}
      {...props}
    />
  )
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4",
        className
      )}
      {...props}
    />
  )
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-title"
      className={cn(
        "font-heading text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
        className
      )}
      {...props}
    />
  )
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-description"
      className={cn("text-sm text-muted-foreground", className)}
      {...props}
    />
  )
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn(
        "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
        className
      )}
      {...props}
    />
  )
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-content"
      className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
      {...props}
    />
  )
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-footer"
      className={cn(
        "flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4",
        className
      )}
      {...props}
    />
  )
}

export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
}
```

### `components/ui/progress.tsx`

```tsx
"use client"

import * as React from "react"
import { Progress as ProgressPrimitive } from "radix-ui"

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

function Progress({
  className,
  value,
  ...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
  return (
    <ProgressPrimitive.Root
      data-slot="progress"
      className={cn(
        "relative flex h-1.5 w-full items-center overflow-x-hidden rounded-full bg-muted",
        className
      )}
      {...props}
    >
      <ProgressPrimitive.Indicator
        data-slot="progress-indicator"
        className="size-full flex-1 bg-primary transition-all"
        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
      />
    </ProgressPrimitive.Root>
  )
}

export { Progress }
```

### `components/ui/badge.tsx`

```tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"

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

const badgeVariants = cva(
  "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
        secondary:
          "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
        destructive:
          "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
        outline:
          "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
        ghost:
          "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
        link: "text-primary underline-offset-4 hover:underline",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

function Badge({
  className,
  variant = "default",
  asChild = false,
  ...props
}: React.ComponentProps<"span"> &
  VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
  const Comp = asChild ? Slot.Root : "span"

  return (
    <Comp
      data-slot="badge"
      data-variant={variant}
      className={cn(badgeVariants({ variant }), className)}
      {...props}
    />
  )
}

export { Badge, badgeVariants }
```
