# Empty State 06 - Chart Data Empty State

> A simple card-based empty state with a chart-themed icon, title, description, and primary connection CTA button, perfect for dashboards before metric data sources are connected.

A shadcn/ui block from the [@shadcn-space](https://shadcnspace.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-space/empty-state-06).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-space/empty-state-06
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-space/empty-state-06-light-ee75e3eb33168f2a.png
- Registry item: https://shadcnspace.com/r/empty-state-06.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnspace.com/r/empty-state-06.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: `lucide-react`, `motion`
- Registry items: `button`, `empty`, `card`

## Files

### `src/components/shadcn-space/blocks/empty-state-06/page.tsx`

```tsx
import EmptyState from "@/components/shadcn-space/blocks/empty-state-06/empty-state";

const Page = () => {
  return <EmptyState />;
};

export default Page;
```

### `src/components/shadcn-space/blocks/empty-state-06/empty-state.tsx`

```tsx
"use client";

import { useRef } from "react";
import type { ReactNode } from "react";
import { motion, useInView, type Variants } from "motion/react";
import { Plug, ChartNoAxesColumn } from "lucide-react";
import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
  EmptyContent,
} from "@/components/ui/empty";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";

interface EmptyStateAction {
  label: string;
  icon?: ReactNode;
  onClick?: () => void;
}

interface EmptyStateProps {
  icon?: ReactNode;
  title?: string;
  description?: string;
  primaryAction?: EmptyStateAction;
  secondaryAction?: EmptyStateAction;
}

const EASE = [0.16, 1, 0.3, 1] as const;

const containerVariants: Variants = {
  hidden: {},
  show: {
    transition: {
      staggerChildren: 0.08,
      delayChildren: 0.05,
    },
  },
};

const itemVariants: Variants = {
  hidden: { opacity: 0, y: 12 },
  show: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.5, ease: EASE },
  },
};

const EmptyState = ({
  icon = <ChartNoAxesColumn />,
  title = "No chart data available",
  description = "Connect a data source to start visualizing your metrics here.",
  primaryAction = {
    label: "Connect data source",
    icon: <Plug className="size-3.5" />,
  },
  secondaryAction,
}: EmptyStateProps) => {
  const ref = useRef<HTMLDivElement>(null);
  const isInView = useInView(ref, { once: true, amount: 0.2 });

  return (
    <div ref={ref} className="w-full py-10 sm:py-16 px-4 flex items-center justify-center">
      <motion.div
        variants={containerVariants}
        initial="hidden"
        animate={isInView ? "show" : "hidden"}
        className="w-full max-w-md animate-in fade-in-0 duration-300"
      >
        <Card className="w-full bg-background ring-0 border-2 border-dashed">
          <CardContent className="p-6">
            <Empty>
              <EmptyHeader>
                <motion.div variants={itemVariants}>
                  <EmptyMedia variant="icon">
                    {icon}
                  </EmptyMedia>
                </motion.div>
                <motion.div
                  variants={itemVariants}
                  className="flex flex-col gap-1.5"
                >
                  <EmptyTitle className="text-base font-semibold text-card-foreground">
                    {title}
                  </EmptyTitle>
                  <EmptyDescription className="text-sm">
                    {description}
                  </EmptyDescription>
                </motion.div>
              </EmptyHeader>

              <motion.div variants={itemVariants} className="w-full">
                <EmptyContent>
                  <div className="flex items-center gap-2">
                    {secondaryAction && (
                      <motion.div
                        whileHover={{ scale: 1.03 }}
                        whileTap={{ scale: 0.97 }}
                        transition={{ duration: 0.2, ease: EASE }}
                        className="inline-flex"
                      >
                        <Button
                          variant="outline"
                          onClick={secondaryAction.onClick}
                          className="gap-1.5 cursor-pointer"
                        >
                          {secondaryAction.icon}
                          {secondaryAction.label}
                        </Button>
                      </motion.div>
                    )}
                    {primaryAction && (
                      <motion.div
                        whileHover={{ scale: 1.03 }}
                        whileTap={{ scale: 0.97 }}
                        transition={{ duration: 0.2, ease: EASE }}
                        className="inline-flex"
                      >
                        <Button
                          onClick={primaryAction.onClick}
                          className="cursor-pointer gap-1.5 hover:bg-primary/80"
                        >
                          {primaryAction.icon}
                          {primaryAction.label}
                        </Button>
                      </motion.div>
                    )}
                  </div>
                </EmptyContent>
              </motion.div>
            </Empty>
          </CardContent>
        </Card>
      </motion.div>
    </div>
  );
};

export default EmptyState;
```
