tutoriales·15 min de lectura

cómo implementar búsqueda instantanea con Fuse.js en NextJS

guía completa para implementar un sistema de búsqueda fuzzy rápido y eficiente usando Fuse.js en tu aplicación NextJS. Incluye ejemplos prácticos y mejores prácticas.

cómo implementar búsqueda instantanea con Fuse.js en NextJS

Si tu sitio NextJS tiene contenido que los usuarios necesitan encontrar rápidamente, necesitas un buen sistema de búsqueda. Fuse.js te permite implementar una búsqueda instantanea y tolerante a errores directamente en el navegador, sin depender de servicios externos. En esta guía vas a aprender a configurar fuse.js en NextJS desde cero, con ejemplos que puedes copiar y adaptar a tu proyecto.

¿Qué es Fuse.js?

Fuse.js es una librería de JavaScript liviana para hacer búsqueda difusa (en ingles fuzzy search). A diferencia de una búsqueda exacta, la búsqueda difusa encuentra resultados incluso cuando el usuario comete errores al escribir o no recuerda exactamente lo que busca. Puedes revisar la documentación oficial de Fuse.js para ver todas las opciones disponibles.

Ejemplo práctico

Si buscas "nxtjs routr", Fuse.js puede encontrar "NextJS Router" porque entiende que probablemente quisiste escribir eso.

¿Por qué usar Fuse.js?

Ventajas:

  1. Tolerante a errores: Encuentra resultados aunque haya errores al escribir
  2. rápido: búsqueda en milisegundos, todo del lado del cliente (en el navegador)
  3. Sin servidor necesario: No necesitas un servidor de búsqueda como Algolia
  4. Ligero: Solo 3KB comprimido
  5. Flexible: Control total sobre que se busca y como

Casos de uso ideales:

  • búsqueda en documentación
  • búsqueda de productos en e-commerce pequeño/mediano
  • búsqueda en blogs
  • Autocompletado de comandos
  • búsqueda en catalogos

Fuse.js es perfecto para hasta ~10,000 items. Si tienes más datos, considera soluciones como Algolia o Elasticsearch.

Instalación

1

Instala Fuse.js

bash
npm install fuse.js
# o
yarn add fuse.js
# o
pnpm add fuse.js
2

Verifica la instalación

Revisa tu package.json y deberías ver:

json
{
  "dependencies": {
    "fuse.js": "^7.1.0"
  }
}

Tu primera búsqueda con Fuse.js

Empecemos con un ejemplo simple para entender cómo funciona.

Ejemplo básico

typescript
import Fuse from 'fuse.js'
 
// Datos de ejemplo
const libros = [
  { título: 'El Quijote', autor: 'Miguel de Cervantes' },
  { título: 'Cien años de soledad', autor: 'Gabriel Garcia Marquez' },
  { título: 'La sombra del viento', autor: 'Carlos Ruiz Zafon' },
]
 
// Crear instancia de Fuse
const fuse = new Fuse(libros, {
  keys: ['título', 'autor']
})
 
// Buscar
const resultado = fuse.search('garsia')
console.log(resultado)
// Encuentra "Gabriel Garcia Marquez" aunque "Garcia" este mal escrito

Fuse.js encontro "Garcia" aunque lo escribimos "garsia". Eso es búsqueda fuzzy!

Configuración de Fuse.js

Fuse.js tiene muchas opciones de configuración. Veamos las más importantes.

Opciones principales

typescript
const fuse = new Fuse(datos, {
  // Campos donde buscar
  keys: ['título', 'descripción', 'tags'],
 
  // threshold (umbral): que tan estricta es la búsqueda
  // 0.0 = solo coincidencias exactas, 1.0 = todo coincide
  threshold: 0.3,
 
  // Distancia máxima entre caracteres coincidentes
  distance: 100,
 
  // Incluir puntuación de relevancia en resultados
  includeScore: true,
 
  // Incluir que partes del texto coincidieron
  includeMatches: true,
 
  // Longitud mínima de búsqueda antes de empezar a buscar
  minMatchCharLength: 2,
})

Explicación de opciones críticas

keys - Campos de búsqueda

Define en que campos buscar:

typescript
// búsqueda simple
keys: ['título']
 
// múltiples campos
keys: ['título', 'descripción', 'autor']
 
// Con pesos (mayor peso = más importante)
keys: [
  { name: 'título', weight: 2 },      // El título es más importante
  { name: 'descripción', weight: 1 },
  { name: 'tags', weight: 0.5 },      // Los tags menos importantes
]

Usa pesos cuando algunos campos son más relevantes que otros. Por ejemplo, coincidencias en el título son más importantes que en la descripción.

threshold - Que tan estricta es la búsqueda (Umbral de tolerancia)

Controla que tan similar debe ser el texto para considerarse una coincidencia. Es un valor entre 0.0 y 1.0.

typescript
threshold: 0.0  // Solo coincidencias exactas
threshold: 0.3  // Balance recomendado (el valor por defecto es 0.6)
threshold: 0.6  // Mas tolerante a errores
threshold: 1.0  // Todo coincide (no es útil)

guía práctica:

  • 0.0 - 0.2: Muy estricto, casi exacto
  • 0.3 - 0.4: Balance ideal para la mayoria de casos
  • 0.5 - 0.6: Tolerante, puede dar resultados no esperados
  • 0.7+: Demasiado tolerante

distance - Distancia entre caracteres

Define que tan lejos pueden estar los caracteres coincidentes.

typescript
distance: 100  // Default, bueno para textos cortos
distance: 1000 // Mejor para textos largos

includeScore - Ver puntuación de relevancia

útil para entender que tan buena es cada coincidencia o para debugging (depuración):

typescript
includeScore: true
 
// Resultado incluye:
{
  item: { título: 'NextJS' },
  score: 0.001  // Menor puntuación = mejor coincidencia
}

Implementando búsqueda en NextJS

Vamos a crear un sistema de búsqueda completo para un blog o documentación.

Paso 1: Crear el índice de búsqueda

Primero, necesitas un archivo con todos los datos para buscar.

typescript
// lib/search-index.ts
export interface SearchItem {
  id: string
  title: string
  description: string
  url: string
  category: string
  keywords: string[]
}
 
export const searchIndex: SearchItem[] = [
  {
    id: 'nextjs-intro',
    title: 'Introducción a NextJS',
    description: 'Aprende los fundamentos de NextJS y cómo crear tu primera aplicación',
    url: '/docs/nextjs/intro',
    category: 'NextJS',
    keywords: ['nextjs', 'react', 'framework', 'introducción'],
  },
  {
    id: 'react-hooks',
    title: 'React Hooks explicados',
    description: 'guía completa de useState, useEffect y otros hooks de React',
    url: '/docs/react/hooks',
    category: 'React',
    keywords: ['react', 'hooks', 'useState', 'useEffect'],
  },
  {
    id: 'typescript-basics',
    title: 'TypeScript para principiantes',
    description: 'Conceptos básicos de TypeScript que todo desarrollador debe conocer',
    url: '/docs/typescript/basics',
    category: 'TypeScript',
    keywords: ['typescript', 'tipos', 'javascript'],
  },
  // Agrega más items...
]

Para proyectos grandes, puedes generar este índice automáticamente leyendo tus archivos MDX o consultando tu base de datos.

Paso 2: Crear el componente de búsqueda

typescript
// components/Search.tsx
"use client"
 
import { useState, useEffect, useRef } from 'react'
import { useRouter } from 'next/navigation'
import Fuse from 'fuse.js'
import { searchIndex, SearchItem } from '@/lib/search-index'
 
// Configurar Fuse
const fuse = new Fuse(searchIndex, {
  keys: [
    { name: 'title', weight: 2 },
    { name: 'description', weight: 1.5 },
    { name: 'keywords', weight: 1 },
    { name: 'category', weight: 0.5 },
  ],
  threshold: 0.3,
  includeScore: true,
})
 
export default function Search() {
  const [query, setQuery] = useState('')
  const [results, setResults] = useState<SearchItem[]>([])
  const [isOpen, setIsOpen] = useState(false)
  const inputRef = useRef<HTMLInputElement>(null)
  const router = useRouter()
 
  // Manejar búsqueda
  const handleSearch = (searchQuery: string) => {
    setQuery(searchQuery)
 
    if (searchQuery.trim() === '') {
      setResults([])
      return
    }
 
    const searchResults = fuse.search(searchQuery)
    const items = searchResults.slice(0, 8).map((result) => result.item)
    setResults(items)
  }
 
  // Abrir con Cmd+K
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault()
        setIsOpen(true)
      }
    }
 
    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [])
 
  // Focus cuando se abre
  useEffect(() => {
    if (isOpen && inputRef.current) {
      inputRef.current.focus()
    }
  }, [isOpen])
 
  if (!isOpen) return null
 
  return (
    <div className="fixed inset-0 z-50 flex items-start justify-center pt-[10vh] px-4">
      {/* Overlay */}
      <div
        className="absolute inset-0 bg-black/50"
        onClick={() => setIsOpen(false)}
      />
 
      {/* Modal */}
      <div className="relative w-full max-w-2xl bg-white rounded-lg shadow-2xl">
        {/* Input */}
        <div className="p-4 border-b">
          <input
            ref={inputRef}
            type="text"
            placeholder="Buscar..."
            value={query}
            onChange={(e) => handleSearch(e.target.value)}
            className="w-full text-lg outline-none"
          />
        </div>
 
        {/* Resultados */}
        <div className="max-h-[60vh] overflow-y-auto p-2">
          {results.length > 0 ? (
            results.map((result) => (
              <button
                key={result.id}
                onClick={() => {
                  router.push(result.url)
                  setIsOpen(false)
                }}
                className="w-full text-left p-3 hover:bg-gray-100 rounded"
              >
                <div className="font-medium">{result.title}</div>
                <div className="text-sm text-gray-600">{result.description}</div>
                <div className="text-xs text-gray-400 mt-1">{result.category}</div>
              </button>
            ))
          ) : query ? (
            <div className="p-8 text-center text-gray-500">
              No se encontraron resultados
            </div>
          ) : (
            <div className="p-8 text-center text-gray-500">
              Escribe para buscar...
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

Paso 3: Agregar el componente al layout

typescript
// app/layout.tsx
import Search from '@/components/Search'
 
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Search />
        {children}
      </body>
    </html>
  )
}

Ahora puedes presionar Cmd+K (o Ctrl+K) desde cualquier página y buscar instantaneamente.

Caracteristicas avanzadas

Resaltar coincidencias

Muestra que parte del texto coincidio con la búsqueda:

typescript
const fuse = new Fuse(datos, {
  includeMatches: true,  // Habilitar matches
  keys: ['título', 'descripción']
})
 
const resultado = fuse.search('nextjs')
 
// resultado contiene información de matches
resultado.forEach(({ item, matches }) => {
  matches?.forEach((match) => {
    console.log('Campo:', match.key)
    console.log('Indices coincidentes:', match.indices)
    // Puedes usar esto para resaltar texto
  })
})

Componente para resaltar texto

typescript
// components/HighlightText.tsx
interface Props {
  text: string
  indices: [number, number][]
}
 
export default function HighlightText({ text, indices }: Props) {
  if (!indices || indices.length === 0) {
    return <span>{text}</span>
  }
 
  const highlighted: JSX.Element[] = []
  let lastIndex = 0
 
  indices.forEach(([start, end]) => {
    // Texto antes del match
    if (start > lastIndex) {
      highlighted.push(
        <span key={`text-${lastIndex}`}>
          {text.substring(lastIndex, start)}
        </span>
      )
    }
 
    // Texto coincidente (resaltado)
    highlighted.push(
      <mark key={`match-${start}`} className="bg-yellow-200">
        {text.substring(start, end + 1)}
      </mark>
    )
 
    lastIndex = end + 1
  })
 
  // Texto después del último match
  if (lastIndex < text.length) {
    highlighted.push(
      <span key={`text-${lastIndex}`}>
        {text.substring(lastIndex)}
      </span>
    )
  }
 
  return <>{highlighted}</>
}

búsqueda con ordenamiento personalizado

Puedes ordenar resultados por score o criterios personalizados:

typescript
const resultado = fuse.search('nextjs')
 
// Ordenar por score (mejor primero)
const ordenado = resultado.sort((a, b) => (a.score || 0) - (b.score || 0))
 
// Ordenar por categoría y luego por score
const ordenadoCustom = resultado.sort((a, b) => {
  // Priorizar categoría "NextJS"
  if (a.item.category === 'NextJS' && b.item.category !== 'NextJS') return -1
  if (a.item.category !== 'NextJS' && b.item.category === 'NextJS') return 1
 
  // Si son la misma categoría, ordenar por score
  return (a.score || 0) - (b.score || 0)
})

Filtros combinados

Combina búsqueda fuzzy con filtros exactos:

typescript
const resultado = fuse.search('hooks')
 
// Filtrar solo resultados de categoría "React"
const filtrado = resultado.filter(({ item }) => item.category === 'React')
 
// O filtrar por múltiples categorías
const categorías = ['React', 'NextJS']
const filtradoMultiple = resultado.filter(({ item }) =>
  categorías.includes(item.category)
)

Optimización de rendimiento

1. Crear índice una sola vez

typescript
// Malo - crea índice en cada render
function Search() {
  const fuse = new Fuse(searchIndex, options)
  // ...
}
 
// Bueno - crea índice una vez fuera del componente
const fuse = new Fuse(searchIndex, options)
 
function Search() {
  // usa 'fuse' directamente
}

2. Retrasar la búsqueda para optimizar rendimiento

Evita buscar en cada tecla que el usuario presiona. En su lugar, espera a que termine de escribir. Esta técnica se llama debouncing (anti-rebote) y mejora significativamente el rendimiento. Usa setTimeout para implementarlo.

typescript
import { useState, useEffect } from 'react'
 
function Search() {
  const [query, setQuery] = useState('')
  const [queryRetrasada, setQueryRetrasada] = useState('')
 
  // Esperar 300ms después de que el usuario deje de escribir
  useEffect(() => {
    const timer = setTimeout(() => {
      setQueryRetrasada(query)
    }, 300)
 
    // Limpiar el timer si el usuario sigue escribiendo
    return () => clearTimeout(timer)
  }, [query])
 
  // Buscar solo cuando queryRetrasada cambia (es decir, después del retraso)
  useEffect(() => {
    if (queryRetrasada) {
      const results = fuse.search(queryRetrasada)
      // Actualizar resultados
    }
  }, [queryRetrasada])
 
  return (
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      placeholder="Escribe para buscar..."
    />
  )
}

Con esta técnica, si el usuario escribe "NextJS" rápidamente, solo se ejecutara UNA búsqueda en lugar de 6 (una por cada letra). Esto ahorra recursos y hace la búsqueda mucho más fluida.

3. Limitar resultados

No muestres miles de resultados:

typescript
const resultado = fuse.search(query)
 
// Limitar a 10 resultados
const limitado = resultado.slice(0, 10)

4. Usar useMemo para indices grandes

Si tu índice es muy grande y cambia dinamicamente, usa useMemo para evitar recrearlo en cada renderizado del componente. Si necesitas repasar como funcionan useEffect, useMemo y otros hooks, revisa la guía de ciclo de vida en React.

typescript
import { useMemo } from 'react'
 
function Search() {
  const fuse = useMemo(() => {
    return new Fuse(searchIndex, options)
  }, []) // El array vacio [] significa "crear solo una vez"
 
  // ...
}

useMemo es un hook de React que memoriza (guarda) el resultado de una operación costosa. aquí lo usamos para crear el índice de Fuse una sola vez y reutilizarlo, en lugar de crearlo cada vez que el componente se renderiza.

Generando índice automáticamente

Para sitios grandes, genera el índice automáticamente:

Desde archivos MDX

typescript
// scripts/generate-search-index.ts
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
 
interface SearchItem {
  id: string
  title: string
  description: string
  url: string
  category: string
}
 
function generateSearchIndex() {
  const docsDir = path.join(process.cwd(), 'content', 'docs')
  const searchIndex: SearchItem[] = []
 
  function scanDirectory(dir: string, category: string) {
    const files = fs.readdirSync(dir)
 
    files.forEach((file) => {
      const filePath = path.join(dir, file)
      const stat = fs.statSync(filePath)
 
      if (stat.isDirectory()) {
        scanDirectory(filePath, file)
      } else if (file.endsWith('.mdx')) {
        const content = fs.readFileSync(filePath, 'utf-8')
        const { data } = matter(content)
 
        searchIndex.push({
          id: file.replace('.mdx', ''),
          title: data.title || '',
          description: data.description || '',
          url: `/docs/${category}/${file.replace('.mdx', '')}`,
          category: category,
        })
      }
    })
  }
 
  scanDirectory(docsDir, 'docs')
 
  // Escribir índice
  fs.writeFileSync(
    path.join(process.cwd(), 'lib', 'search-index.json'),
    JSON.stringify(searchIndex, null, 2)
  )
 
  console.log(`Generados ${searchIndex.length} items para búsqueda`)
}
 
generateSearchIndex()

Ejecuta el script:

json
// package.json
{
  "scripts": {
    "generate-search": "tsx scripts/generate-search-index.ts",
    "build": "npm run generate-search && next build"
  }
}

Desde base de datos

Si vas a consultar datos desde una API, Asegúrate de manejar correctamente las llamadas asíncronas. Puedes revisar la guía de async/await en JavaScript para entender el patron.

typescript
// lib/generate-search-index.ts
import { db } from './database'
 
export async function generateSearchIndex() {
  const posts = await db.post.findMany({
    where: { published: true },
    select: {
      id: true,
      title: true,
      excerpt: true,
      slug: true,
      category: true,
      tags: true,
    },
  })
 
  return posts.map((post) => ({
    id: post.id,
    title: post.title,
    description: post.excerpt,
    url: `/blog/${post.slug}`,
    category: post.category,
    keywords: post.tags,
  }))
}

Casos de uso reales

1. búsqueda de comandos

Para una aplicación con muchos comandos:

typescript
const comandos = [
  { nombre: 'crear usuario', descripción: 'Crea un nuevo usuario', atajo: 'Ctrl+U' },
  { nombre: 'borrar usuario', descripción: 'Elimina un usuario existente', atajo: 'Ctrl+D' },
  { nombre: 'editar perfil', descripción: 'Modifica información del perfil', atajo: 'Ctrl+E' },
]
 
const fuse = new Fuse(comandos, {
  keys: ['nombre', 'descripción', 'atajo'],
  threshold: 0.4,
})
 
// Buscar "crer usario" encuentra "crear usuario"

2. búsqueda de productos

Para un e-commerce pequeño:

typescript
const productos = [
  {
    nombre: 'iPhone 15 Pro',
    descripción: 'Smartphone de última generación',
    marca: 'Apple',
    precio: 1299,
    tags: ['movil', 'smartphone', 'apple']
  },
  // ...más productos
]
 
const fuse = new Fuse(productos, {
  keys: [
    { name: 'nombre', weight: 2 },
    { name: 'marca', weight: 1.5 },
    { name: 'tags', weight: 1 },
    { name: 'descripción', weight: 0.5 },
  ],
  threshold: 0.3,
})

3. Autocompletado

typescript
function Autocomplete() {
  const [query, setQuery] = useState('')
  const [suggestions, setSuggestions] = useState<string[]>([])
 
  const handleChange = (value: string) => {
    setQuery(value)
 
    if (value.length < 2) {
      setSuggestions([])
      return
    }
 
    const results = fuse.search(value)
    const titles = results.slice(0, 5).map((r) => r.item.title)
    setSuggestions(titles)
  }
 
  return (
    <div>
      <input value={query} onChange={(e) => handleChange(e.target.value)} />
 
      {suggestions.length > 0 && (
        <ul>
          {suggestions.map((suggestion, i) => (
            <li key={i} onClick={() => setQuery(suggestion)}>
              {suggestion}
            </li>
          ))}
        </ul>
      )}
    </div>
  )
}

Mejores prácticas

1. Manten el índice actualizado

typescript
// Actualizar índice cuando agregues contenido
const nuevoItem = {
  id: 'nuevo-post',
  title: 'Mi nuevo post',
  description: '...',
}
 
searchIndex.push(nuevoItem)
 
// Recrear Fuse con el índice actualizado
const fuse = new Fuse(searchIndex, options)

2. No incluyas contenido sensible

Asegúrate de que tu índice de búsqueda solo contenga datos publicos. Si manejas datos de usuarios, válida la estructura con Zod antes de exponerlos.

typescript
// Malo - incluye datos privados
const searchIndex = [
  {
    title: 'Usuario Admin',
    email: 'admin@ejemplo.com',  // Información sensible
    password: '...',  // Nunca hagas esto!
  }
]
 
// Bueno - solo datos publicos
const searchIndex = [
  {
    title: 'Documentación de API',
    description: 'Aprende a usar nuestra API',
  }
]

3. Optimiza el threshold según tu caso

typescript
// Para búsqueda de código (más estricto)
threshold: 0.2
 
// Para búsqueda de texto general (balanceado)
threshold: 0.3
 
// Para búsqueda muy tolerante
threshold: 0.5

4. Usa paginación para muchos resultados

typescript
const RESULTS_PER_PAGE = 10
 
function SearchResults() {
  const [page, setPage] = useState(0)
  const results = fuse.search(query)
 
  const paginatedResults = results.slice(
    page * RESULTS_PER_PAGE,
    (page + 1) * RESULTS_PER_PAGE
  )
 
  return (
    <div>
      {paginatedResults.map((result) => (
        <ResultItem key={result.item.id} item={result.item} />
      ))}
 
      <button onClick={() => setPage(page + 1)}>
        Cargar más
      </button>
    </div>
  )
}

Troubleshooting común

No encuentra resultados obvios

Problema: Buscas "NextJS" pero no encuentra nada.

Soluciones:

  1. Verifica que el threshold no sea muy bajo:
typescript
threshold: 0.3  // Prueba con 0.4 o 0.5
  1. Revisa que estes buscando en las keys correctas:
typescript
keys: ['title', 'description']  // Asegúrate que incluye los campos correctos
  1. Verifica que los datos existan:
typescript
console.log(searchIndex)  // Debug

búsqueda muy lenta

Problema: La búsqueda tarda varios segundos.

Soluciones:

  1. Limita el tamaño del índice
  2. Usa debouncing
  3. Limita resultados con .slice(0, 10)
  4. No recrees Fuse en cada render

Resultados irrelevantes

Problema: Encuentra cosas que no tienen sentido.

Soluciones:

  1. Baja el threshold:
typescript
threshold: 0.2  // Mas estricto
  1. Ajusta pesos de campos:
typescript
keys: [
  { name: 'title', weight: 3 },  // Mayor peso = más importante
  { name: 'description', weight: 1 },
]

Alternativas a Fuse.js

Si Fuse.js no se ajusta a tu caso:

Para búsquedas más grandes:

Para búsquedas más simples:

  • .filter() nativo de JavaScript
  • Simple string matching con .includes()

Conclusion

Fuse.js es una excelente opción para agregar búsqueda instantanea y tolerante a errores en tu sitio NextJS:

cuándo usar Fuse.js:

  • Tienes menos de 10,000 items
  • Quieres búsqueda del lado del cliente
  • No quieres pagar por servicios externos
  • Necesitas tolerancia a typos

Cuando NO usar Fuse.js:

  • Tienes millones de registros
  • Necesitas búsqueda en tiempo real desde DB
  • Requieres análisis semántico complejo

Para la mayoria de blogs, documentación y sitios medianos, Fuse.js es la solución perfecta: simple, rápida y efectiva. Combinala con un buen sitemap automatico en NextJS para que Google descubra tu contenido y Fuse.js lo haga accesible para tus usuarios.


Recursos adicionales

#fuse.js#búsqueda#nextjs#fuzzy-search

Preguntas frecuentes

¿Qué es la búsqueda fuzzy y por qué es útil en aplicaciones web?

La búsqueda fuzzy (o difusa) es un tipo de búsqueda que encuentra resultados incluso cuando el usuario comete errores de escritura o no recuerda el término exacto. Es útil porque mejora la experiencia del usuario al ser tolerante a typos y variaciones en la forma de escribir.

¿Cómo se configura el threshold de Fuse.js para obtener buenos resultados?

El threshold controla que tan estricta es la búsqueda, con valores entre 0.0 (solo coincidencias exactas) y 1.0 (todo coincide). Un valor de 0.3 a 0.4 es el balance ideal para la mayoria de casos. Puedes ajustarlo según tu necesidad: más bajo para búsquedas de código, más alto para texto general.

¿Fuse.js afecta el rendimiento de mi aplicación NextJS?

Fuse.js es muy ligero (3KB comprimido) y rápido para hasta 10,000 items. Para optimizar rendimiento, crea el índice una sola vez fuera del componente, usa debouncing para evitar búsquedas en cada tecla, y limita los resultados mostrados con slice.