Lucide React Reference

Lucide is a beautiful and consistent icon library with over 1000 icons. All icons are tree-shakeable, meaning only imported icons are included in the bundle.

Import

import {
  Plus,
  Trash2,
  Check,
  Circle,
  Search,
  Filter,
  MoreHorizontal,
} from "lucide-react";

interface Task {
  id: string;
  text: string;
  done: boolean;
}

export default function App() {
  const [tasks, setTasks] = React.useState<Task[]>([]);
  const [input, setInput] = React.useState("");
  const [search, setSearch] = React.useState("");

  const addTask = () => {
    if (!input.trim()) return;
    setTasks(prev => [...prev, { id: crypto.randomUUID(), text: input, done: false }]);
    setInput("");
  };

  const toggleTask = (id: string) => {
    setTasks(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));
  };

  const deleteTask = (id: string) => {
    setTasks(prev => prev.filter(t => t.id !== id));
  };

  const filtered = tasks.filter(t => t.text.toLowerCase().includes(search.toLowerCase()));

  return (
    <div>
      <div>
        <div>
          <h3 className="flex items-center gap-2">
            <Check className="w-5 h-5" />
            Tasks
          </h3>
        </div>
        <div className="space-y-4">
          <div className="flex gap-2">
            <input
              value={input}
              onChange={e => setInput(e.target.value)}
              placeholder="Add task..."
              onKeyDown={e => e.key === "Enter" && addTask()}
            />
            <button onClick={addTask} size="icon">
              <Plus className="w-4 h-4" />
            </button>
          </div>

          <div className="relative">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
            <input
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Search..."
              className="pl-9"
            />
          </div>

          <div className="space-y-2">
            {filtered.map(task => (
              <div key={task.id} className="flex items-center gap-3 p-2 rounded-md hover:bg-muted">
                <button onClick={() => toggleTask(task.id)}>
                  {task.done ? (
                    <Check className="w-5 h-5 text-primary" />
                  ) : (
                    <Circle className="w-5 h-5 text-muted-foreground" />
                  )}
                </button>
                <span className={task.done ? "line-through text-muted-foreground flex-1" : "flex-1"}>
                  {task.text}
                </span>
                <button variant="ghost" size="icon" onClick={() => deleteTask(task.id)}>
                  <Trash2 className="w-4 h-4" />
                </button>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

Best Practices

  1. Use consistent sizing - w-4 h-4 for buttons/list items, w-5 h-5 for headers
  2. Use currentColor - Icons inherit text color by default
  3. Use semantic color tokens - text-primary, text-muted-foreground, text-destructive
  4. Add mr-2 or ml-2 for spacing between icon and text in buttons
  5. Use Loader2 with animate-spin for loading states
  6. Import only the icons you need (do NOT import entire library)