Skip to main content

Best Boilerplates for Analytics Dashboards in 2026

·StarterPick Team
analyticsdashboardchartsboilerplate2026

Analytics Dashboards: Data First, UI Second

The challenge with analytics dashboards isn't the charts — it's the data pipeline. Querying time-series data at scale, aggregating events, and responding in <500ms requires different database patterns than typical CRUD apps.

Quick Comparison

StackPriceChartsTime-Series DBReal-timeBest For
Tremor + Next.jsFree✅ ExcellentPostgreSQL/ClickHouse✅ PusherSaaS analytics dashboards
GrafanaFree (self-host)MultipleInfrastructure/DevOps metrics
PostHogFree-$450/moClickHouseProduct analytics platform
MetabaseFree (self-host)MultipleSQL-query BI dashboards

Tremor — Best React Dashboard Components

Price: Free | Creator: Tremor team

Tailwind CSS dashboard components for Next.js. Area charts, bar charts, line charts, donut charts, metrics cards, tables, and sparklines — all designed for analytics dashboards.

import { AreaChart, Card, Metric, Text, Title } from "@tremor/react";

const data = [
  { date: "Jan 2026", Revenue: 4200, Users: 340 },
  { date: "Feb 2026", Revenue: 5100, Users: 425 },
  { date: "Mar 2026", Revenue: 6800, Users: 580 },
];

export function RevenueChart() {
  return (
    <Card>
      <Title>Monthly Revenue</Title>
      <AreaChart
        className="h-72 mt-4"
        data={data}
        index="date"
        categories={["Revenue", "Users"]}
        colors={["indigo", "cyan"]}
        valueFormatter={(v) => `$${v.toLocaleString()}`}
      />
    </Card>
  );
}

Database Patterns for Analytics

Standard PostgreSQL struggles with analytics queries on large datasets. Solutions:

TimescaleDB (PostgreSQL Extension)

-- TimescaleDB hypertable for time-series data
CREATE TABLE events (
  time        TIMESTAMPTZ NOT NULL,
  user_id     UUID,
  event_type  TEXT,
  properties  JSONB
);

SELECT create_hypertable('events', 'time');  -- Partitions by time automatically

-- Efficient time-bucketed aggregation
SELECT
  time_bucket('1 day', time) AS day,
  event_type,
  COUNT(*) AS event_count
FROM events
WHERE time > NOW() - INTERVAL '30 days'
GROUP BY 1, 2
ORDER BY 1;

ClickHouse (Columnar Database)

-- ClickHouse for billions of events
CREATE TABLE events (
  timestamp DateTime,
  user_id UInt64,
  event_type String,
  properties String  -- JSON
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (event_type, timestamp);

-- 10 billion row query in <1 second
SELECT
  toDate(timestamp) AS date,
  event_type,
  uniq(user_id) AS unique_users,
  count() AS total_events
FROM events
WHERE timestamp BETWEEN '2026-01-01' AND '2026-03-08'
GROUP BY 1, 2
ORDER BY 1;

Building a SaaS Metrics Dashboard

The standard metrics every SaaS needs:

// tRPC: SaaS metrics queries
const metricsRouter = router({
  mrr: protectedProcedure.query(async ({ ctx }) => {
    const activeSubscriptions = await stripe.subscriptions.list({
      status: 'active',
      limit: 100,
    });

    const mrr = activeSubscriptions.data.reduce((sum, sub) =>
      sum + sub.items.data.reduce((s, item) =>
        s + (item.price.unit_amount! * item.quantity! / 100) *
        (item.price.recurring!.interval === 'year' ? 1/12 : 1)
      , 0)
    , 0);

    return { mrr, currency: 'usd' };
  }),

  newUsers: protectedProcedure
    .input(z.object({ days: z.number().default(30) }))
    .query(async ({ input }) => {
      return db.user.groupBy({
        by: ['createdAt'],
        where: { createdAt: { gte: subDays(new Date(), input.days) } },
        _count: true,
      });
    }),
});

Compare analytics dashboard and SaaS boilerplates on StarterPick.

Comments