We use cookies to enhance your experience on the site
CodeWorlds

Visual Search and Shopping Integration

Pinterest is all about discovery through visual search and shopping.

Visual Search

1interface VisualSearchResult {
2  similarPins: Pin[];
3  relatedSearches: string[];
4  shoppingResults?: Product[];
5}
6
7// Search by image
8async function visualSearch(imageUrl: string): Promise<VisualSearchResult> {
9  // Send image to computer vision API
10  // Returns similar images and detected objects/colors
11
12  return {
13    similarPins: await findSimilarPins(imageUrl),
14    relatedSearches: await extractKeywords(imageUrl),
15    shoppingResults: await findProducts(imageUrl)
16  };
17}
18
19// Camera search component
20'use client';
21
22export default function CameraSearch() {
23  const [imagePreview, setImagePreview] = useState<string | null>(null);
24  const [results, setResults] = useState<VisualSearchResult | null>(null);
25  const [isSearching, setIsSearching] = useState(false);
26
27  const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
28    const file = e.target.files?.[0];
29    if (!file) return;
30
31    const reader = new FileReader();
32    reader.onload = (event) => {
33      const imageUrl = event.target?.result as string;
34      setImagePreview(imageUrl);
35      performVisualSearch(imageUrl);
36    };
37    reader.readAsDataURL(file);
38  };
39
40  const performVisualSearch = async (imageUrl: string) => {
41    setIsSearching(true);
42    const searchResults = await visualSearch(imageUrl);
43    setResults(searchResults);
44    setIsSearching(false);
45  };
46
47  return (
48    <div className="min-h-screen bg-white p-8">
49      <div className="max-w-6xl mx-auto">
50        {/* Upload Area */}
51        {!imagePreview ? (
52          <div className="text-center">
53            <h1 className="text-4xl font-bold mb-8">Search by image</h1>
54
55            <label className="inline-block cursor-pointer">
56              <div className="w-64 h-64 border-4 border-dashed border-gray-300 rounded-2xl flex flex-col items-center justify-center hover:border-red-500 transition">
57                <div className="text-6xl mb-4">📷</div>
58                <div className="font-semibold mb-2">Upload an image</div>
59                <div className="text-sm text-gray-600">or drag and drop</div>
60              </div>
61              <input
62                type="file"
63                accept="image/*"
64                onChange={handleImageUpload}
65                className="hidden"
66              />
67            </label>
68          </div>
69        ) : (
70          <div>
71            {/* Image Preview */}
72            <div className="mb-8">
73              <img
74                src={imagePreview}
75                alt="Search query"
76                className="max-w-sm mx-auto rounded-2xl shadow-lg"
77              />
78            </div>
79
80            {/* Loading */}
81            {isSearching && (
82              <div className="text-center text-gray-600 mb-8">
83                Searching for similar pins...
84              </div>
85            )}
86
87            {/* Results */}
88            {results && (
89              <div>
90                {/* Related Searches */}
91                <div className="mb-8">
92                  <h2 className="text-2xl font-bold mb-4">Related searches</h2>
93                  <div className="flex flex-wrap gap-2">
94                    {results.relatedSearches.map(search => (
95                      <button
96                        key={search}
97                        className="px-4 py-2 bg-gray-200 rounded-full hover:bg-gray-300"
98                      >
99                        {search}
100                      </button>
101                    ))}
102                  </div>
103                </div>
104
105                {/* Similar Pins */}
106                <div>
107                  <h2 className="text-2xl font-bold mb-4">Similar pins</h2>
108                  <MasonryGrid pins={results.similarPins} />
109                </div>
110              </div>
111            )}
112          </div>
113        )}
114      </div>
115    </div>
116  );
117}

Shopping Integration

1interface Product {
2  id: string;
3  name: string;
4  price: number;
5  currency: string;
6  imageUrl: string;
7  merchant: {
8    name: string;
9    logo: string;
10  };
11  inStock: boolean;
12  rating?: number;
13  reviewCount?: number;
14}
15
16// Product pin
17interface ProductPin extends Pin {
18  product: Product;
19  isShoppable: true;
20}
21
22// Shopping pin card
23function ShoppingPinCard({ pin }: { pin: ProductPin }) {
24  return (
25    <div className="relative rounded-2xl overflow-hidden group cursor-pointer">
26      <img src={pin.imageUrl} alt={pin.title} className="w-full h-auto" />
27
28      {/* Price Badge */}
29      <div className="absolute top-4 left-4 px-3 py-1 bg-white rounded-full font-bold shadow-lg">
30        ${pin.product.price}
31      </div>
32
33      {/* Hover Overlay */}
34      <div className="absolute inset-0 bg-black bg-opacity-40 p-4 flex flex-col justify-between opacity-0 group-hover:opacity-100 transition">
35        <div className="flex justify-end gap-2">
36          <button className="px-4 py-2 bg-white rounded-full font-semibold hover:bg-gray-100">
37            Save
38          </button>
39          <button className="px-4 py-2 bg-blue-600 text-white rounded-full font-semibold hover:bg-blue-700">
40            Shop
41          </button>
42        </div>
43
44        <div className="bg-white p-3 rounded-2xl">
45          <div className="flex items-center gap-2 mb-2">
46            <img
47              src={pin.product.merchant.logo}
48              alt={pin.product.merchant.name}
49              className="w-6 h-6 rounded"
50            />
51            <span className="text-sm font-semibold">
52              {pin.product.merchant.name}
53            </span>
54          </div>
55
56          <h3 className="font-bold mb-1">{pin.product.name}</h3>
57
58          {pin.product.rating && (
59            <div className="flex items-center gap-1 text-sm">
60              <span>{pin.product.rating.toFixed(1)}</span>
61              <span className="text-gray-600">
62                ({pin.product.reviewCount} reviews)
63              </span>
64            </div>
65          )}
66
67          {!pin.product.inStock && (
68            <div className="text-red-600 text-sm font-semibold mt-2">
69              Out of stock
70            </div>
71          )}
72        </div>
73      </div>
74    </div>
75  );
76}

Summary 🎓

Visual search - upload image to find similar pins ✅ Image upload - drag and drop or file picker ✅ Related searches - keyword extraction from image ✅ Similar pins - computer vision matching ✅ Shopping pins - product integration ✅ Price badge - show price on pins ✅ Merchant info - seller details ✅ Shop button - direct purchase link ✅ Stock status - availability indicator

You now have a complete Pinterest clone with masonry grid, boards, visual search and shopping! 📍

Congratulations! You've completed all 14 modules of Vibecoding! 🎉

See you there! 🚀

Go to CodeWorlds