-50% CODE

BLOOM50

|

Simple Search Input

Lightweight search component with real-time filtering and dropdown results.

ReactTailwind CSSTypeScriptResponsiveInput

Live Preview

Interactive
Simple Search Input.tsxLive Code
"use client";

import { useEffect, useRef, useState } from "react";

interface SearchResult {
  id: string;
  title: string;
  category: string;
}

export default function SimpleSearchInput() {
  const [query, setQuery] = useState("");
  const [isOpen, setIsOpen] = useState(false);
  const [results, setResults] = useState<SearchResult[]>([]);

  const inputRef = useRef<HTMLInputElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);

  const sampleResults: SearchResult[] = [
    { id: "1", title: "Button Component", category: "Components" },
    { id: "2", title: "Card Component", category: "Components" },
    { id: "3", title: "Modal Component", category: "Components" },
    { id: "4", title: "Landing Page", category: "Templates" },
  ];

  const performSearch = (searchQuery: string) => {
    if (!searchQuery.trim()) {
      setResults([]);
      return;
    }

    const filteredResults = sampleResults.filter((item) =>
      item.title.toLowerCase().includes(searchQuery.toLowerCase())
    );
    setResults(filteredResults);
  };

  useEffect(() => {
    performSearch(query);
  }, [query]);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        dropdownRef.current &&
        !dropdownRef.current.contains(event.target as Node) &&
        !inputRef.current?.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  }, []);

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setQuery(e.target.value);
    setIsOpen(true);
  };

  const clearSearch = () => {
    setQuery("");
    setResults([]);
    inputRef.current?.focus();
  };

  return (
    <div className="w-full max-w-md mx-auto relative">
      <div className="relative">
        <div className="absolute left-3 top-1\/2 transform -translate-y-1\/2 text-gray-400">
          <svg
            className="w-4 h-4"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
            \/>
          <\/svg>
        <\/div>

        <input
          ref={inputRef}
          type="text"
          value={query}
          onChange={handleInputChange}
          onFocus={() => setIsOpen(true)}
          placeholder="Search..."
          className="w-full pl-10 pr-10 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none"
        \/>

        {query && (
          <button
            type="button"
            onClick={clearSearch}
            className="absolute right-3 top-1\/2 transform -translate-y-1\/2 text-gray-400 hover:text-gray-600"
          >
            <svg
              className="w-4 h-4"
              fill="none"
              stroke="currentColor"
              viewBox="0 0 24 24"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M6 18L18 6M6 6l12 12"
              \/>
            <\/svg>
          <\/button>
        )}
      <\/div>

      {isOpen && (
        <div
          ref={dropdownRef}
          className="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-60 overflow-y-auto"
        >
          {!query ? (
            <div className="p-3 text-center text-gray-500 text-sm">
              Start typing to search...
            <\/div>
          ) : results.length > 0 ? (
            <div className="p-1">
              {results.map((result) => (
                <button
                  key={result.id}
                  onClick={() => {
                    setIsOpen(false);
                    console.log(`Selected: ${result.title}`);
                  }}
                  className="w-full text-left p-2 hover:bg-gray-50 rounded flex items-center justify-between"
                >
                  <span className="font-medium">{result.title}<\/span>
                  <span className="text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded">
                    {result.category}
                  <\/span>
                <\/button>
              ))}
            <\/div>
          ) : (
            <div className="p-3 text-center text-gray-500 text-sm">
              No results found
            <\/div>
          )}
        <\/div>
      )}
    <\/div>
  );
}

How to use

  1. 1. Copy the component code
  2. 2. Paste it into your React/Next.js project
  3. 3. Make sure you have Tailwind CSS configured
  4. 4. Customize colors, spacing, and content as needed

Dependencies

react^18.0.0
tailwindcss^3.0.0