HomeBrew Apartments
Buscador sin servidor de 100 alquileres de corta estancia en Madrid, Berlín y París, con búsqueda debounced, lista de guardados y publicación.

Problema
Los portales de alquiler suelen estar saturados de anuncios y tener una búsqueda lenta. Quería una experiencia de exploración rápida y centrada para una única fuente de anuncios.
Enfoque
Una aplicación de página única en React sin servidor, sin base de datos y
sin API: el catálogo de 100 anuncios viaja con la app como un archivo
JSON, y todo lo que guardas o publicas se almacena en el localStorage del
navegador. Esa restricción da forma a casi todo el diseño. Buscas por
ciudad o país, abres un anuncio para ver sus fotos, datos, desglose de
precio y anfitrión, guardas los que te gustan y puedes publicar un anuncio
propio en la misma cuadrícula.
Arquitectura
- Estado:
App.jsxes la única fuente de verdad. No hay contexto ni store — el estado baja por props, de forma deliberada, para una app de este tamaño - El catálogo se carga mediante un hook
useCatalogueque importa dinámicamente el JSON de anuncios, manteniéndolo fuera del chunk inicial - Los resultados de búsqueda se derivan, nunca se almacenan: se recalculan a partir del conjunto completo de anuncios más una consulta debounced en cada render
- Persistencia: los anuncios guardados son ids en
localStorage, y los objetos se resuelven contra el catálogo completo; los anuncios creados por el usuario se anteponen a él - Estilos: CSS Modules por componente sobre un sistema de diseño en
tokens.css— ningún valor hexadecimal ni número mágico de píxeles fuera de ahí
Decisiones clave
- Derivar los resultados en lugar de almacenarlos corrigió un fallo real:
una versión anterior los guardaba en su propio estado y se desincronizaban
del campo de búsqueda. Por lo mismo, las búsquedas de un anuncio van
contra el catálogo completo y nunca contra la vista filtrada, que devuelve
undefinedpara todo lo que la búsqueda actual excluye ApartmentCardestá memoizado y recibe un booleano plano en vez de un predicadoisFavorite, cuya identidad cambia en cada alternancia y volvería a renderizar las 100 tarjetas- El catálogo se genera en tiempo de compilación a partir del volcado de datos original, descartando en torno al 60% de campos que nada renderiza — el archivo generado se versiona para que un clon nuevo funcione al instante
- La serif de display lleva el logotipo y todos los números — precios, valoraciones, recuentos por ciudad — mientras que el texto de interfaz usa la pila de fuentes del sistema
Resultados
Un buscador responsivo sobre 100 anuncios con búsqueda por ciudad y país insensible a mayúsculas y con debounce, un contador por ciudad en vivo sobre la cuadrícula que hace además de chips de filtro y de control de reinicio, una lista de guardados y anuncios publicados que sobreviven a una recarga, y temas claro y oscuro que siguen la preferencia del sistema hasta que eliges uno. Una suite en Vitest cubre los fallos que ocurrieron de verdad, y el linter corre sin tolerar ni un aviso.
Qué haría diferente
Paginaría o virtualizaría la cuadrícula — el filtrado en el cliente lo
necesitaría con un conjunto de datos mucho mayor que este. La limitación
más importante es localStorage: los anuncios guardados y publicados nunca
salen del navegador donde se crearon, así que un backend real es el
siguiente paso, no una optimización.