All components

Product Quickviews 01

Product Quickviews 01 is a React and Tailwind CSS component from Bund UI, licensed MIT. Copy it and paste it into your project.

Bund UI MIT other

What it is

Product Quickviews 01 is a React and Tailwind CSS component from Bund UI, licensed MIT. Copy it and paste it into your project.

How to use it

Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.

Open in the catalogue

Five free copies a month. No card.

Needs

npm i lucide-react

Licence

This component comes from Bund UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.

Product Quickviews 01

examples/blocks/ecommerce/product-quickviews/01/components/product-quickview-modal.tsx
"use client";

import { useState } from "react";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { HeartIcon, Star, X } from "lucide-react";
import { cn } from "@/lib/utils";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";

interface ProductQuickviewProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  product: {
    id: string;
    title: string;
    price: number;
    rating: number;
    reviewCount: number;
    image: string;
    colors: Array<{ name: string; value: string }>;
    sizes: Array<{ name: string; available: boolean }>;
  };
}

export function ProductQuickviewDialog({ open, onOpenChange, product }: ProductQuickviewProps) {
  const [selectedColor, setSelectedColor] = useState(product.colors[0]?.name);
  const [selectedSize, setSelectedSize] = useState("S");

  const renderStars = (rating: number) => {
    return Array(5)
      .fill("")
      .map((_, i) =>
        i < rating ? (
          <Star key={i} className="size-4 fill-amber-500 text-amber-500 lg:size-5" />
        ) : (
          <Star key={i} className="text-muted-foreground size-4 lg:size-5" />
        )
      );
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="gap-0 overflow-hidden p-0 sm:max-w-4xl">
        <div className="flex grid-cols-1 flex-col gap-0 md:grid md:grid-cols-2">
          {/* Product Image */}
          <figure className="flex items-center justify-center">
            <img
              src={product.image}
              alt={product.title}
              className="aspect-square h-full w-full object-cover"
            />
          </figure>

          {/* Product Details */}
          <div className="flex flex-col p-4 md:p-8">
            <div className="flex-1">
              <h2 className="font-heading mb-4 text-2xl text-balance lg:text-3xl">
                {product.title}
              </h2>

              <p className="mb-4 text-2xl font-medium lg:mb-6">${product.price}</p>

              {/* Rating */}
              <div className="mb-6 flex items-center gap-3 lg:mb-8">
                <div className="flex gap-1">{renderStars(product.rating)}</div>
                <a href="#" className="text-sm font-medium text-indigo-600 hover:text-indigo-700">
                  {product.reviewCount} reviews
                </a>
              </div>

              {/* Color Selection */}
              <fieldset className="mb-4 space-y-4 lg:mb-8">
                <legend className="text-foreground text-sm leading-none font-medium">Color</legend>
                <RadioGroup
                  className="flex gap-1.5"
                  onValueChange={(value) => setSelectedColor(value)}
                  defaultValue="blue">
                  {product.colors.map((color) => (
                    <RadioGroupItem
                      value={color.name}
                      style={{ backgroundColor: color.value }}
                      aria-label={`Select ${color.name} color`}
                      className={cn(
                        "size-6 rounded-full border-1 transition-all",
                        selectedColor === color.name
                          ? "border-foreground ring-foreground ring ring-offset-2"
                          : "border-border hover:border-foreground/50"
                      )}
                    />
                  ))}
                </RadioGroup>
              </fieldset>

              {/* Size Selection */}
              <fieldset className="mb-4 space-y-4 lg:mb-8">
                <legend className="text-foreground flex w-full items-center justify-between text-sm leading-none font-medium">
                  <span>Size</span>
                  <a
                    href="#size-guide"
                    className="text-sm font-medium text-indigo-600 hover:text-indigo-700">
                    Size guide
                  </a>
                </legend>
                <RadioGroup
                  className="grid grid-cols-4 gap-2"
                  onValueChange={(value) => setSelectedSize(value)}
                  defaultValue="1">
                  {product.sizes.map((size) => (
                    <label
                      key={`${size.name}`}
                      className="border-input has-focus-visible:border-ring has-focus-visible:ring-ring/50 has-data-[state=checked]:border-primary/50 relative flex cursor-pointer flex-col items-center gap-3 rounded-md border px-2 py-3 text-center shadow-xs transition-[color,box-shadow] outline-none has-focus-visible:ring-[3px] has-data-disabled:cursor-not-allowed has-data-disabled:opacity-50">
                      <RadioGroupItem
                        id={`${size.name}`}
                        value={size.name}
                        className="sr-only after:absolute after:inset-0"
                      />
                      <p className="text-foreground text-sm leading-none font-medium">
                        {size.name}
                      </p>
                    </label>
                  ))}
                </RadioGroup>
              </fieldset>
            </div>

            {/* Add to Bag Button */}
            <div className="flex gap-2">
              <Button size="lg" className="grow">
                Add to Cart
              </Button>
              <Button variant="outline" size="lg">
                <HeartIcon />
              </Button>
            </div>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  );
}
examples/blocks/ecommerce/product-quickviews/01/page.tsx
"use client";

import { ProductQuickviewDialog } from "./components/product-quickview-modal";
import Link from "next/link";
import Image from "next/image";
import { useState } from "react";
import { Button } from "@/components/ui/button";

const productData = {
  id: "1",
  title: "Skater Jeans",
  price: 49,
  rating: 4,
  reviewCount: 117,
  image: "/images/products/list2.png",
  colors: [
    { name: "White", value: "#FFFFFF" },
    { name: "Gray", value: "#9CA3AF" },
    { name: "Crimson", value: "crimson" },
    { name: "Navy", value: "#1E293B" }
  ],
  sizes: [
    { name: "XXS", available: true },
    { name: "XS", available: true },
    { name: "S", available: true },
    { name: "M", available: true },
    { name: "L", available: true },
    { name: "XL", available: true },
    { name: "XXL", available: true },
    { name: "XXXL", available: false }
  ]
};

export default function Page() {
  const [open, setOpen] = useState(true);

  return (
    <>
      <div className="mx-auto max-w-80 py-60 md:py-24">
        <Link href="#" className="group">
          <figure className="relative aspect-square w-full overflow-hidden rounded-md object-cover">
            <Image
              fill
              className="object-cover transition-transform duration-300 group-hover:scale-105 group-hover:rotate-2"
              src={productData.image}
              alt={productData.title}
            />
          </figure>
          <div className="mt-3 space-y-2">
            <div className="flex items-center justify-between gap-1">
              <p className="font-medium">{productData.title}</p>
              <p className="text-muted-foreground">{productData.price}</p>
            </div>
          </div>
          <Button onClick={() => setOpen(true)} variant="secondary" className="mt-4 w-full">
            Quick View
          </Button>
        </Link>
      </div>
      <ProductQuickviewDialog open={open} onOpenChange={setOpen} product={productData} />
    </>
  );
}

Similar components