Back to roadmaps lucide-icons Course

Project: Icon Search Directory

In this project, we will construct an interactive icon search directory. The interface allows users to filter icons dynamically in real-time and click any icon card to copy its import code.


1. Directory Specifications

  • Search Input: Triggers keyup events to filter matching icon cards.
  • Dynamic Grid: Displays filtered card components showing the icon name and SVG shape.
  • Clipboard Copy: Copies JSX code (e.g., <Home />) to the clipboard on click.

2. Implementing the Directory Interface

Here is the React code. Save this inside src/components/IconSearch.tsx:

import React, { useState } from "react";
import { 
  Search, 
  Home, 
  Settings, 
  User, 
  Trash2, 
  Edit, 
  Copy, 
  Check 
} from "lucide-react";

interface IconItem {
  name: string;
  component: React.ComponentType<{ className?: string }>;
}

export function IconSearch() {
  const [query, setQuery] = useState("");
  const [copiedIndex, setCopiedIndex] = useState<number | null>(null);

  const iconsList: IconItem[] = [
    { name: "Home", component: Home },
    { name: "Settings", component: Settings },
    { name: "User", component: User },
    { name: "Trash", component: Trash2 },
    { name: "Edit", component: Edit },
  ];

  // Filter list based on search query string
  const filteredIcons = iconsList.filter((icon) =>
    icon.name.toLowerCase().includes(query.toLowerCase())
  );

  // Copy code snippet to clipboard
  const handleCopyCode = (name: string, index: number) => {
    const codeText = `<${name} className="w-5 h-5" />`;
    navigator.clipboard.writeText(codeText);
    
    setCopiedIndex(index);
    setTimeout(() => setCopiedIndex(null), 1500);
  };

  return (
    <div className="max-w-4xl mx-auto p-8">
      <h1 className="text-3xl font-bold">Icon Search Directory</h1>
      
      {/* Search Input bar */}
      <div className="relative mt-6">
        <span className="absolute inset-y-0 left-0 pl-3 flex items-center">
          <Search className="w-5 h-5 text-gray-400" />
        </span>
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search icons by name..."
          className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
        />
      </div>

      {/* Grid displays icon cards */}
      <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-6 mt-8">
        {filteredIcons.map((icon, index) => {
          const IconComponent = icon.component;
          const isCopied = copiedIndex === index;

          return (
            <button
              key={index}
              onClick={() => handleCopyCode(icon.name, index)}
              className="flex flex-col items-center justify-center p-6 bg-white border border-gray-200 rounded-xl hover:border-blue-500 hover:shadow-md transition relative group"
            >
              <IconComponent className="w-8 h-8 text-gray-700 group-hover:text-blue-600 transition" />
              <span className="text-sm text-gray-500 mt-3">{icon.name}</span>
              
              {/* Copy indicator pill overlay */}
              <span className="absolute top-2 right-2 p-1.5 rounded-lg bg-gray-50 text-gray-400 group-hover:bg-blue-50 group-hover:text-blue-600 transition">
                {isCopied ? <Check className="w-4 h-4 text-green-600" /> : <Copy className="w-4 h-4" />}
              </span>
            </button>
          );
        })}
      </div>
    </div>
  );
}
Published on Last updated: