Búsqueda difusa en cliente con Fuse.js
¿Alguna vez escribiste mal una palabra al buscar en una web y no obtuviste ningún resultado? La búsqueda exacta es despiadada si el texto no coincide por algún carácter, simplemente no aparece.
Pero no tiene por qué ser así. Aquí es donde entra Fuse.js, una librería de JavaScript que permite realizar búsquedas difusas (fuzzy search) en el navegador, sin necesidad de un servidor.
❓¿Qué es la búsqueda difusa?
Es una forma de buscar que tolera errores de escritura, diferencias de formato o coincidencias parciales. Por ejemplo, si un usuario escribe krug y tú tienes Krüg Grande Cuvée, Fuse.js lo encuentra.
🤔 ¿Por qué usar Fuse.js?
- Corre completamente en el navegador.
- No necesitas un servicio externo como Algolia.
- Se puede personalizar la búsqueda ajustando sensibilidad, campos a buscar, y más.
- Súper rápido, incluso con cientos o miles de elementos.
- Tiene documentación disponible en: fusejs.io
🤔 ¿Cuando usar Fuse.js?
Usa Fuse.js cuando tienes control total sobre el conjunto de datos y necesitas ofrecer una búsqueda rápida, flexible y tolerante a errores… sin depender de un backend.
¿La clave? Que los datos estén en el cliente, ya sea porque el volumen es pequeño o porque puedes cachearlos eficientemente.
Esto permite que las búsquedas se hagan sobre el total de datos en memoria, sin llamadas a servidores, sin latencias, y con una experiencia instantánea para el usuario.
❌ No lo uses si:
- El volumen de datos es muy grande y no lo puedes manejar todo en el cliente.
- No puedes controlar cómo o cuándo se cargan o actualizan los datos.
- Necesitas paginación o consultas parciales dinámicas.
🤖 Ejemplo simple de Fuse.js
Vamos a explicar en pasos simples como utilizar la dependencia.
// 1. Importamos la librería Fuse.js
import Fuse from 'fuse.js'
// 2. Definimos una lista de objetos (vinos)
const wines = [
{ name: 'Krüg Grande Cuvée' },
{ name: 'Moët & Chandon Brut' },
{ name: 'Dom Pérignon 2012' }
]
/* 3. Creamos una instancia de Fuse. Le pasamos la data y
especificamos que queremos buscar por la key 'name' */
const fuse = new Fuse(wines, {
keys: ['name']
// threshold: 0.4 // sensibilidad, 0 = exacto y 1 = no estricto
})
// 4. Realizamos una búsqueda con la palabra 'kru'
const result = fuse.search('kru')
// 5. Mostramos el resultado en consola
console.log(result)
/*
Resultado esperado:
[
{
"item":{"name":"Krüg Grande Cuvée"}, // el objeto encontrado
"refIndex":0 // posición del ítem original en el array
}
]
*/
🙌 Código del ejemplo interactivo
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<title>Buscador de vinos con Fuse.js</title>
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.6.2"></script>
<style>
body {
font-family: sans-serif;
padding: 20px;
}
input {
padding: 8px;
width: 300px;
font-size: 16px;
margin-bottom: 8px;
}
ul {
margin-top: 10px;
padding-left: 0;
}
li {
list-style: none;
margin-bottom: 5px;
}
</style>
</head>
<body>
<h2>Buscar vino</h2>
<input type="text" id="searchInput" placeholder="Escribe para buscar..." />
<ul id="results"></ul>
<script>
const wines = [
{ name: 'Krüg Grande Cuvée' },
{ name: 'Moët & Chandon Brut' },
{ name: 'Dom Pérignon 2012' },
{ name: 'Ruinart Rosé' },
{ name: 'Veuve Clicquot Brut' },
{ name: 'Baron B Extra Brut' }
]
const fuse = new Fuse(wines, {
keys: ['name'],
threshold: 0.4
})
const input = document.getElementById('searchInput')
const resultsList = document.getElementById('results')
function renderResults(data) {
resultsList.innerHTML = ''
data.forEach((item) => {
const name = item.name || item.item?.name
const li = document.createElement('li')
li.textContent = name
resultsList.appendChild(li)
})
}
renderResults(wines)
input.addEventListener('input', () => {
const query = input.value.trim()
const results = query ? fuse.search(query) : wines
renderResults(results)
})
</script>
</body>
</html>
Array wines con más propiedades
const wines = [
{
name: 'Krüg Grande Cuvée',
category: 'Champagne',
year: 2015,
cellars: ['Bodega Krug', 'Francia', 'Metodo tradicional']
},
{
name: 'Moët & Chandon Brut',
category: 'Champagne',
year: 2018,
cellars: ['Bodega Moët & Chandon', 'Francia', 'Brut']
},
{
name: 'Dom Pérignon 2012',
category: 'Champagne',
year: 2012,
cellars: ['Bodega Dom Pérignon', 'Francia', 'Vintage']
},
{
name: 'Ruinart Rosé',
category: 'Rosé',
year: 2020,
cellars: ['Bodega Ruinart', 'Francia', 'Rosé']
},
{
name: 'Veuve Clicquot Brut',
category: 'Champagne',
year: 2016,
cellars: ['Bodega Veuve Clicquot', 'Francia', 'Brut']
},
{
name: 'Baron B Extra Brut',
category: 'Sparkling',
year: 2019,
others: [
{
label: 'cellar',
value: 'La bodega'
},
{
label: 'year',
value: '2019'
}
]
}
]
Mostrar más información en el listado
const renderResults = (results) => {
resultsList.innerHTML = ''
results.forEach((item) => {
const li = document.createElement('li')
li.textContent = `${item.name} - ${item.category} - Año: ${item.year}` // AJUSTAMOS
resultsList.appendChild(li)
})
}
Buscar sobre un valor numérico
const fuse = new Fuse(wines, {
keys: [
{
name: 'year',
getFn: (obj) => obj.year.toString()
}
],
threshold: 0.3
})
Buscar sobre un sub array de string
const fuse = new Fuse(wines, {
keys: ['cellars'],
threshold: 0.3
})
Buscar sobre un sub array de objetos
const fuse = new Fuse(wines, {
keys: [
{
name: 'cellar',
getFn: (obj) => {
return obj?.others?.find((o) => o.label === 'year')?.value
}
}
],
threshold: 0.3
})
✅ Conclusión
Implementar búsqueda difusa en el cliente con Fuse.js es una forma rápida y efectiva de mejorar la experiencia del usuario si tienes el control del conjunto total de datos en clientes.
Permite encontrar resultados relevantes incluso con errores de escritura o coincidencias parciales.
Es ligera, configurable y perfecta para aplicaciones modernas que buscan ofrecer búsquedas más humanas y tolerantes.
¡Gracias por leer!