bloomtpl
  • Documentation
  • Contact
bloomtpl

The best Next.js templates for modern web applications.
Deploy faster, code less.

Your Feedback

Review us onTrustpilot

Templates

  • All Templates10+
  • Business Templates
  • E-Commerce Templates
  • Landing Page Templates
  • Blog Templates
  • Portfolio Templates
  • Free Templates

Resources

  • Docs
  • Contact
  • Blog
  • License

Follow us

Twitter

Legal

  • Privacy Policy
  • Refund Policy
  • Terms of Use
  • Legal Notice
© 2025 BloomTPL™. All Rights Reserved.
❤️Made with love for developers
ComponentsSimple Search Input

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
tailwindcss
Back to Components

Need complete templates?

View Premium Templates