# Analytics 1

> A weekly audience growth statistics widget showcasing active readers and email leads, complete with a highlighted weekly bar chart.

A shadcn/ui block from the [@pacekit](https://ui.pacekit.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/pacekit/app-analytics-1).

- Page, with a live preview: https://shadcn.exchange/r/pacekit/app-analytics-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/pacekit/app-analytics-1-light-43f535f0b1c70cf2.png
- Registry item: https://ui.pacekit.dev/r/app-analytics-1.json
- Listed: 2026-10-21, source last changed 2026-10-21

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/pacekit/app-analytics-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: `recharts`
- Registry items: `card`, `badge`, `chart`, `button`, `dropdown-menu`

## Files

### `components/blocks/app/analytics/analytics-1.tsx`

```tsx
import { BookOpen, ChevronUp, Mail, MoreVertical, RefreshCw, Settings } from "lucide-react";
import { Bar, BarChart, Cell, XAxis } from "recharts";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart";
import {
    DropdownMenu,
    DropdownMenuContent,
    DropdownMenuItem,
    DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

const chartConfig = {
    normal: {
        label: "Subscribers",
        color: "var(--chart-2)",
    },
    highlighted: {
        label: "Peak Subscribers",
        color: "var(--chart-1)",
    },
} satisfies ChartConfig;

const chartData = [
    { day: "Mo", value: 120 },
    { day: "Tu", value: 150 },
    { day: "We", value: 135 },
    { day: "Th", value: 160 },
    { day: "Fr", value: 245 },
    { day: "Sa", value: 98 },
    { day: "Su", value: 110 },
];

const metrics = [
    {
        title: "Active Readers",
        subtitle: "Premium members",
        value: "2,840",
        badgeText: "14.2%",
        icon: BookOpen,
    },
    {
        title: "Newsletter Leads",
        subtitle: "Email campaigns",
        value: "918",
        badgeText: "32.1%",
        icon: Mail,
    },
];

export const Analytics1 = () => {
    return (
        <Card className="gap-4">
            <CardHeader>
                <CardTitle>Audience Growth</CardTitle>
                <CardDescription>Weekly subscription trends</CardDescription>
                <CardAction>
                    <DropdownMenu>
                        <DropdownMenuTrigger
                            render={
                                <Button variant="ghost" size="icon-xs" className="cursor-pointer">
                                    <MoreVertical className="size-4" />
                                </Button>
                            }></DropdownMenuTrigger>
                        <DropdownMenuContent align="end" className="w-40">
                            <DropdownMenuItem className="cursor-pointer gap-2 text-xs">
                                <RefreshCw className="size-3.5" />
                                <span>Refresh</span>
                            </DropdownMenuItem>
                            <DropdownMenuItem className="cursor-pointer gap-2 text-xs">
                                <Settings className="size-3.5" />
                                <span>Settings</span>
                            </DropdownMenuItem>
                        </DropdownMenuContent>
                    </DropdownMenu>
                </CardAction>
            </CardHeader>
            <CardContent className="space-y-4">
                <div className="bg-muted/40 grid grid-cols-[1fr_auto] items-center gap-4 rounded-md border px-4 py-2.5">
                    <div className="grid gap-0.5">
                        <p className="text-3xl font-medium tracking-tight">3,758</p>
                        <p className="text-muted-foreground text-xs">Total weekly active subscribers</p>
                    </div>
                    <Badge variant="outline">
                        <ChevronUp className="size-3" />
                        <span>+18.4%</span>
                    </Badge>
                </div>
                <div className="grid gap-4">
                    {metrics.map((item, idx) => {
                        const IconComponent = item.icon;
                        return (
                            <div key={idx} className="grid grid-cols-[1fr_auto] items-center gap-4">
                                <div className="flex items-center gap-3">
                                    <div className={`bg-muted flex size-10 items-center justify-center rounded-md`}>
                                        <IconComponent className="size-5" />
                                    </div>
                                    <div className="grid gap-0.5">
                                        <span className="text-sm font-medium">{item.title}</span>
                                        <span className="text-muted-foreground text-xs">{item.subtitle}</span>
                                    </div>
                                </div>
                                <div className="flex items-center gap-2">
                                    <span className="text-sm font-medium">{item.value}</span>
                                    <Badge>
                                        <ChevronUp className="size-3" />
                                        <span>{item.badgeText}</span>
                                    </Badge>
                                </div>
                            </div>
                        );
                    })}
                </div>
                <div className="h-36 w-full">
                    <ChartContainer config={chartConfig} className="h-full w-full">
                        <BarChart data={chartData} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
                            <XAxis
                                dataKey="day"
                                tickLine={false}
                                axisLine={false}
                                tickMargin={8}
                                className="text-muted-foreground text-xs"
                            />
                            <ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
                            <Bar dataKey="value" radius={[4, 4, 0, 0]}>
                                {chartData.map((entry, index) => (
                                    <Cell
                                        key={entry.day}
                                        fill={index === 4 ? "var(--color-highlighted)" : "var(--color-normal)"}
                                        className="cursor-pointer"
                                    />
                                ))}
                            </Bar>
                        </BarChart>
                    </ChartContainer>
                </div>
            </CardContent>
        </Card>
    );
};
```

### `hooks/use-mobile.ts`

```ts
import { useEffect, useState } from "react";

const MOBILE_BREAKPOINT = 768;

export function useIsMobile() {
    const [isMobile, setIsMobile] = useState<boolean | undefined>(undefined);

    useEffect(() => {
        const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
        const onChange = () => {
            setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
        };
        mql.addEventListener("change", onChange);
        setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
        return () => mql.removeEventListener("change", onChange);
    }, []);

    return !!isMobile;
}
```
