Skip to content
All Taste components

Dashboards

Analytics overview

Website traffic at a glance: visitors against the previous period, where they came from and the pages that convert.

top nav · 4 KPIs · area chart with comparison · donut · pages table with bars

Pulse
MN
Visitors
1,28,460
↑ 18.2% vs prior 30 days
Page views
4,02,115
↑ 9.6% vs prior 30 days
Bounce rate
41.3%
↑ 2.1 pts vs prior 30 days
Avg. visit
2m 48s
↑ 14s vs prior 30 days

Visitors

16 Sep – 15 OctPrevious period
02k4k6kSat 10 Oct5,61216 Sep23 Sep30 Sep7 Oct15 Oct

Top sources

42%
from search
Search53,950
Social34,680
Direct23,120
Email16,710

Top pages

PageShare of visitorsVisitorsAvg. timeConversion
/
61,2401m 12s2.8%
/collections/filter-coffee
28,1903m 05s6.4%
/products/malabar-pepper-200g
19,0302m 41s8.1%
/blog/how-to-store-spices
13,4804m 22s1.2%
import { Calendar, ChevronDown, Globe } from "lucide-react";
import { cn } from "@/lib/utils";

const tabs = ["Overview", "Pages", "Sources", "Funnels", "Events"];

const kpis = [
  { label: "Visitors", value: "1,28,460", change: "↑ 18.2%", good: true },
  { label: "Page views", value: "4,02,115", change: "↑ 9.6%", good: true },
  { label: "Bounce rate", value: "41.3%", change: "↑ 2.1 pts", good: false },
  { label: "Avg. visit", value: "2m 48s", change: "↑ 14s", good: true },
];

// Daily visitors in thousands, this period and the previous one.
const visits = [3.1, 3.4, 3.2, 3.8, 4.1, 3.0, 2.7, 3.6, 3.9, 4.2, 4.0, 4.6, 3.4, 3.1, 4.3, 4.8, 4.5, 5.1, 4.9, 3.8, 3.6, 4.9, 5.3, 5.0, 5.6, 5.4, 4.2, 4.0, 5.5, 5.9];
const previous = [2.8, 3.0, 2.9, 3.2, 3.4, 2.6, 2.4, 3.1, 3.3, 3.5, 3.4, 3.7, 2.9, 2.7, 3.6, 3.8, 3.7, 4.0, 3.9, 3.1, 2.9, 3.9, 4.1, 4.0, 4.2, 4.1, 3.4, 3.2, 4.2, 4.4];

const sources = [
  { name: "Search", visitors: "53,950", share: 42, swatch: "bg-primary" },
  { name: "Social", visitors: "34,680", share: 27, swatch: "bg-primary/60" },
  { name: "Direct", visitors: "23,120", share: 18, swatch: "bg-primary/30" },
  { name: "Email", visitors: "16,710", share: 13, swatch: "bg-surface-2" },
];

const pages = [
  { path: "/", share: 100, visitors: "61,240", time: "1m 12s", conversion: "2.8%" },
  { path: "/collections/filter-coffee", share: 46, visitors: "28,190", time: "3m 05s", conversion: "6.4%" },
  { path: "/products/malabar-pepper-200g", share: 31, visitors: "19,030", time: "2m 41s", conversion: "8.1%" },
  { path: "/blog/how-to-store-spices", share: 22, visitors: "13,480", time: "4m 22s", conversion: "1.2%" },
];

function toPath(values: number[], max = 7) {
  return values.map((v, i) => (i ? "L" : "M") + (i / (values.length - 1)) * 100 + " " + (100 - (v / max) * 100)).join(" ");
}

export default function AnalyticsPage() {
  const line = toPath(visits);
  return (
    <div className="min-h-screen bg-bg font-sans text-sm text-fg">
      <header className="flex h-14 items-center gap-5 border-b border-border px-4 sm:px-6">
        <span className="flex items-center gap-2 font-medium">
          <span className="grid size-6 place-items-center rounded-sm bg-primary text-xs font-semibold text-primary-fg">P</span>Pulse
        </span>
        <nav aria-label="Sections" className="hidden h-full gap-4 md:flex">
          {tabs.map((t, i) => (
            <a key={t} href="#" aria-current={i === 0 ? "page" : undefined}
              className={cn("flex items-center border-b-2 border-transparent text-[13px] font-medium text-fg-muted hover:text-fg",
                i === 0 && "border-primary text-fg")}>{t}</a>
          ))}
        </nav>
        <div className="ml-auto flex items-center gap-2">
          <button type="button" className="hidden h-8 items-center gap-1.5 rounded border border-border-strong px-2.5 text-[13px] font-medium sm:inline-flex">
            <Globe className="size-3.5" aria-hidden /> shop.kaveri.in <ChevronDown className="size-3.5" aria-hidden />
          </button>
          <button type="button" className="inline-flex h-8 items-center gap-1.5 rounded border border-border-strong px-2.5 text-[13px] font-medium">
            <Calendar className="size-3.5" aria-hidden /> Last 30 days <ChevronDown className="size-3.5" aria-hidden />
          </button>
        </div>
      </header>

      <main className="grid gap-4 p-4 sm:p-6">
        <h1 className="sr-only">Analytics overview</h1>
        <section aria-label="Key figures" className="grid grid-cols-2 gap-3 xl:grid-cols-4">
          {kpis.map((k) => (
            <div key={k.label} className="rounded-lg border border-border p-4">
              <p className="text-xs text-fg-muted">{k.label}</p>
              <p className="mt-1 text-2xl font-medium tracking-tight tabular-nums">{k.value}</p>
              <p className="mt-1 text-xs">
                <span className={cn("font-medium tabular-nums", k.good ? "text-success-fg" : "text-danger-fg")}>{k.change}</span>
                <span className="text-fg-subtle"> vs prior 30 days</span>
              </p>
            </div>
          ))}
        </section>

        <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_300px]">
          <section aria-labelledby="visitors-title" className="rounded-lg border border-border">
            <div className="flex flex-wrap items-center gap-3 border-b border-border px-4 py-3">
              <h2 id="visitors-title" className="font-medium">Visitors</h2>
              <div className="ml-auto flex gap-3 text-xs text-fg-muted">
                <span className="flex items-center gap-1.5"><i className="size-2 rounded-sm bg-primary" />16 Sep – 15 Oct</span>
                <span className="flex items-center gap-1.5"><i className="size-2 rounded-sm bg-border-strong" />Previous period</span>
              </div>
            </div>
            <svg viewBox="0 0 100 100" preserveAspectRatio="none" className="h-56 w-full p-4" role="img" aria-label="Daily visitors over the last 30 days">
              <defs>
                <linearGradient id="fill" x1="0" x2="0" y1="0" y2="1">
                  <stop offset="0" className="[stop-color:var(--tst-primary)] [stop-opacity:.22]" />
                  <stop offset="1" className="[stop-color:var(--tst-primary)] [stop-opacity:0]" />
                </linearGradient>
              </defs>
              <path d={line + " L100 100 L0 100 Z"} fill="url(#fill)" />
              <path d={toPath(previous)} fill="none" vectorEffect="non-scaling-stroke" strokeDasharray="4 3" className="stroke-border-strong" strokeWidth={1.5} />
              <path d={line} fill="none" vectorEffect="non-scaling-stroke" className="stroke-primary" strokeWidth={2} />
            </svg>
          </section>

          <section aria-labelledby="sources-title" className="rounded-lg border border-border">
            <h2 id="sources-title" className="border-b border-border px-4 py-3 font-medium">Top sources</h2>
            <ul className="grid gap-3 p-4">
              {sources.map((s) => (
                <li key={s.name} className="grid gap-1.5">
                  <div className="flex items-center gap-2">
                    <i className={cn("size-2 rounded-sm", s.swatch)} />{s.name}
                    <span className="ml-auto tabular-nums text-fg-muted">{s.visitors}</span>
                  </div>
                  <div className="h-1.5 rounded-full bg-surface-2"><div className={cn("h-full rounded-full", s.swatch)} style={{ width: s.share + "%" }} /></div>
                </li>
              ))}
            </ul>
          </section>
        </div>

        <section aria-labelledby="pages-title" className="overflow-hidden rounded-lg border border-border">
          <h2 id="pages-title" className="border-b border-border px-4 py-3 font-medium">Top pages</h2>
          <div className="overflow-x-auto">
            <table className="w-full text-[13px]">
              <thead className="bg-surface text-left text-xs text-fg-subtle">
                <tr>
                  <th className="px-4 py-2 font-medium">Page</th><th className="w-1/3 px-4 py-2 font-medium">Share of visitors</th>
                  <th className="px-4 py-2 text-right font-medium">Visitors</th><th className="px-4 py-2 text-right font-medium">Avg. time</th>
                  <th className="px-4 py-2 text-right font-medium">Conversion</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-border">
                {pages.map((p) => (
                  <tr key={p.path} className="whitespace-nowrap">
                    <td className="px-4 py-2.5 font-medium">{p.path}</td>
                    <td className="px-4 py-2.5"><div className="h-1.5 rounded-full bg-surface-2"><div className="h-full rounded-full bg-primary" style={{ width: p.share + "%" }} /></div></td>
                    <td className="px-4 py-2.5 text-right tabular-nums">{p.visitors}</td>
                    <td className="px-4 py-2.5 text-right tabular-nums text-fg-muted">{p.time}</td>
                    <td className="px-4 py-2.5 text-right font-medium tabular-nums">{p.conversion}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </section>
      </main>
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.