I menu a tendina sono uno dei pattern più diffusi nell’interfaccia utente. Spesso, però, la loro usabilità crolla quando la lista delle opzioni diventa troppo lunga. In questi casi, un dropdown ricercabile può fare la differenza, permettendo all’utente di filtrare velocemente i risultati. In questo tutorial vedremo come costruire da zero un componente di questo tipo in React, senza dipendere da librerie esterne come react-select
.
Setup iniziale
Partiamo da un progetto creato con Vite o con create-react-app
. Supponiamo di avere già la struttura base con App.jsx
pronta.
npm create vite@latest searchable-dropdown
cd searchable-dropdown
npm install
npm run dev
Creazione del componente
Creiamo un nuovo file SearchableDropdown.jsx
all’interno della cartella src/components/
.
L’idea di base è:
- Un campo
input
per digitare la ricerca. - Una lista filtrata di opzioni.
- Una logica per selezionare e mostrare l’elemento scelto.
Ecco la prima bozza del codice:
import { useState } from "react";
export default function SearchableDropdown({ options, onSelect }) {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const filteredOptions = options.filter(option =>
option.toLowerCase().includes(query.toLowerCase())
);
const handleSelect = (option) => {
setQuery(option);
setIsOpen(false);
onSelect(option);
};
return (
<div className="dropdown">
<input
type="text"
value={query}
placeholder="Seleziona..."
onChange={(e) => setQuery(e.target.value)}
onFocus={() => setIsOpen(true)}
/>
{isOpen && (
<ul className="dropdown-menu">
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => (
<li key={index} onClick={() => handleSelect(option)}>
{option}
</li>
))
) : (
<li className="no-results">Nessun risultato</li>
)}
</ul>
)}
</div>
);
}
Gestione della selezione
Il componente invia al parent la voce scelta tramite la callback onSelect
. Ecco come usarlo in App.jsx
:
import SearchableDropdown from "./components/SearchableDropdown";
function App() {
const options = ["React", "Vue", "Angular", "Svelte", "SolidJS"];
const handleSelection = (value) => {
console.log("Hai selezionato:", value);
};
return (
<div style={{ padding: "2rem" }}>
<h1>Dropdown ricercabile</h1>
<SearchableDropdown options={options} onSelect={handleSelection} />
</div>
);
}
export default App;
Ora digitando nell’input vedremo filtrare in tempo reale la lista di opzioni.
Miglioramenti
Un dropdown funzionale deve gestire alcuni casi aggiuntivi:
- Chiusura al click esterno: possiamo usare un
useEffect
condocument.addEventListener("click")
per intercettare i click fuori dal componente. - Navigazione da tastiera: aggiungendo supporto a
ArrowUp
,ArrowDown
edEnter
. - Accessibilità (ARIA): attribuire ruoli come
role="listbox"
erole="option"
. - Stile CSS: con qualche riga di CSS possiamo ottenere un look moderno.
Esempio di chiusura al click esterno:
import { useState, useRef, useEffect } from "react";
export default function SearchableDropdown({ options, onSelect }) {
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const ref = useRef();
const filteredOptions = options.filter(option =>
option.toLowerCase().includes(query.toLowerCase())
);
const handleSelect = (option) => {
setQuery(option);
setIsOpen(false);
onSelect(option);
};
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
return (
<div className="dropdown" ref={ref}>
<input
type="text"
value={query}
placeholder="Seleziona..."
onChange={(e) => setQuery(e.target.value)}
onFocus={() => setIsOpen(true)}
/>
{isOpen && (
<ul className="dropdown-menu">
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => (
<li key={index} onClick={() => handleSelect(option)}>
{option}
</li>
))
) : (
<li className="no-results">Nessun risultato</li>
)}
</ul>
)}
</div>
);
}
Conclusioni
Abbiamo visto come costruire da zero un dropdown ricercabile in React, evitando dipendenze esterne e mantenendo il pieno controllo sul comportamento del componente. Questo approccio è modulare, facilmente estendibile e può essere adattato a scenari più complessi, come la selezione multipla o l’integrazione con API remote.