SearchInput
searchinput-1779378817631.tsx
'use client'
import { useState, useEffect, useRef } from 'react'
const SUGGESTIONS = ['GlowButton', 'HoverTiltCard', 'CommandMenu', 'DataTable', 'LineChart', 'PricingCards', 'ChatInterface', 'MagicLinkAuth', 'DrawerPanel', 'StatsGrid', 'OTPInput', 'TestimonialCarousel', 'ToastStack', 'ConfirmModal', 'TabsNavigation']
const RECENT = ['GlowButton', 'DataTable', 'ChatInterface']
export default function SearchInput() {
const [value, setValue] = useState('')
const [open, setOpen] = useState(false)
const [cursor, setCursor] = useState(-1)
const ref = useRef<HTMLInputElement>(null)
const filtered = value.length > 0
? SUGGESTIONS.filter(s => s.toLowerCase().includes(value.toLowerCase())).slice(0, 6)
: []
const items = value.length > 0 ? filtered : RECENT
function select(item: string) {
setValue(item)
setOpen(false)
setCursor(-1)
}
function onKey(e: React.KeyboardEvent) {
if (e.key === 'ArrowDown') { e.preventDefault(); setCursor(c => Math.min(c + 1, items.length - 1)) }
if (e.key === 'ArrowUp') { e.preventDefault(); setCursor(c => Math.max(c - 1, -1)) }
if (e.key === 'Enter' && cursor >= 0) select(items[cursor])
if (e.key === 'Escape') { setOpen(false); setValue('') }
}
return (
<div style={{ padding: 40, background: '#0A0A0A', display: 'flex', justifyContent: 'center', minHeight: 200 }}>
<div style={{ position: 'relative', width: 360 }}>
<div style={{ position: 'relative' }}>
<span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 14, color: 'rgba(255,255,255,0.3)' }}>🔍</span>
<input ref={ref} value={value}
onChange={e => { setValue(e.target.value); setOpen(true); setCursor(-1) }}
onFocus={() => setOpen(true)}
onBlur={() => setTimeout(() => setOpen(false), 150)}
onKeyDown={onKey}
placeholder="Search components..."
style={{ width: '100%', boxSizing: 'border-box', background: 'rgba(255,255,255,0.04)', border: open ? '1.5px solid rgba(201,168,76,0.4)' : '1.5px solid rgba(255,255,255,0.1)', borderRadius: 12, color: '#F5F5F0', fontSize: 14, padding: '12px 40px 12px 36px', outline: 'none', transition: 'border-color 0.2s' }}
/>
{value && <button onClick={() => { setValue(''); ref.current?.focus() }} style={{ position: 'absolute', right: 10, top: '50%', transform: 'translateY(-50%)', background: 'rgba(255,255,255,0.08)', border: 'none', color: 'rgba(255,255,255,0.5)', borderRadius: '50%', width: 20, height: 20, cursor: 'pointer', fontSize: 12, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>×</button>}
</div>
{open && (
<div style={{ position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, background: '#111', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 12, overflow: 'hidden', boxShadow: '0 16px 48px rgba(0,0,0,0.6)', zIndex: 9999 }}>
{value === '' && <div style={{ padding: '8px 14px 4px', color: 'rgba(255,255,255,0.3)', fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Recent</div>}
{items.length === 0 && <div style={{ padding: '14px', color: 'rgba(255,255,255,0.35)', fontSize: 13 }}>No results for "{value}"</div>}
{items.map((item, i) => (
<div key={item} onClick={() => select(item)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', cursor: 'pointer', background: cursor === i ? 'rgba(255,255,255,0.06)' : 'transparent', transition: 'background 0.1s' }}>
<span style={{ fontSize: 13, color: 'rgba(255,255,255,0.25)' }}>{value.length > 0 ? '⬡' : '↻'}</span>
<span style={{ color: '#F5F5F0', fontSize: 13 }}>
{value.length > 0 ? (
<>
{item.substring(0, item.toLowerCase().indexOf(value.toLowerCase()))}
<mark style={{ background: 'rgba(201,168,76,0.25)', color: '#C9A84C', borderRadius: 2 }}>{item.substring(item.toLowerCase().indexOf(value.toLowerCase()), item.toLowerCase().indexOf(value.toLowerCase()) + value.length)}</mark>
{item.substring(item.toLowerCase().indexOf(value.toLowerCase()) + value.length)}
</>
) : item}
</span>
</div>
))}
{value.length > 0 && filtered.length > 0 && (
<div style={{ padding: '8px 14px', borderTop: '1px solid rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.3)', fontSize: 12 }}>
Press Enter to search all results
</div>
)}
</div>
)}
</div>
</div>
)
}Component info
CategoryInputs & Forms
Frameworkreact
TierFREE
Views0
Copies0
About
Search input with debounced suggestions, keyboard navigation, recent searches, and clear button
More from Inputs & Forms
'use client';
import { useState, useEffect } from 'react';
interface Tag {
id: number;
name: string;
}
const TagInput = () => {
const [tags, setTags] = useState<Tag[]>([]);
const [inputValue, setInputValue] = useState('');
const [suggestTagInput
Inputs & Forms
'use client';
import React, { useState } from 'react';
interface File {
name: string;
size: number;
progress: number;
}
const FileUpload = () => {
const [dragOver, setDragOver] = useState(false);
const [files, setFiles] = useState<File[]FileUpload
Inputs & Forms
import React, { useState } from 'react';
const OTPInput = () => {
const [otp, setOtp] = useState(new Array(6).fill(''));
const [activeIndex, setActiveIndex] = useState(0);
const handleChange = (e, index) => {
const value = e.target.value;OTPInput
Inputs & Forms
'use client';
import React, { useState } from 'react';
interface RangeSliderProps {
min: number;
max: number;
defaultValue: [number, number];
}
const RangeSlider: React.FC<RangeSliderProps> = ({ min, max, defaultValue }) => {
const [minValRangeSlider
Inputs & Forms