Points of Tango: Frontend Engineering for a Global Tango Event Discovery & Community Platform

Project Overview
The global tango community is passionate but fragmented. Thousands of milongas, festivals, classes, and practicas happen every year across dozens of countries — yet they live on disconnected local websites, Facebook groups, and incomplete community directories. Dancers travelling to a new city have no reliable way to find local events. Organisers waste hours updating listings across multiple platforms, only to reach the same loyal audience.
Points of Tango was built to solve exactly this: a unified, globally-scoped event discovery and community management platform available on web, iOS, and Android. The platform serves two distinct user types — dancers who want to find and attend events, and organisers who want to manage listings, registrations, and community presence.
I joined the project as the frontend developer, responsible for the web application's event discovery experience, map-based geo exploration, and the organiser-facing dashboard — built entirely in Vue.js / Nuxt.js.
Business Outcomes
- Global event centralisation: The platform aggregates tango events worldwide, replacing the fragmented multi-directory experience with a single source of truth for the global community.
- Organiser self-service: The organiser dashboard enables promoters to independently list, update, and manage their events without relying on platform administrators — reducing operational overhead and improving listing freshness.
- Geo-aware discovery: Map-based exploration lets travelling dancers instantly surface events near their current or planned destination, directly addressing the core pain point of community fragmentation.
- Free-to-access model with B2B monetisation: The platform is free for all dancers and organisers at the base level, with premium features (featured listings, marketing campaigns, showroom) available under B2B subscription agreements — a freemium model that lowers adoption barriers while creating revenue upside.
- Cross-platform reach: Web application paired with iOS and Android native apps ensures the platform reaches both desk-bound organisers and mobile-first dancers in the field.
What I Built
1. Event Discovery & Listings UI
Designed and built the primary event browsing experience — the core surface of the platform. This included:
- Filterable, searchable event listings by type (milonga, class, practica, festival, concert, exhibition, theatre show, documentary)
- Event detail pages with full metadata: date, time, location, organiser info, description, and registration CTA
- Dynamic list rendering from REST API responses, with loading states, empty states, and error handling
- Responsive layouts optimised for both desktop research and mobile browsing on-the-go
2. Organiser Dashboard
Built the full organiser-facing management interface, giving event promoters control over their presence on the platform:
- Event creation and editing forms with multi-field validation
- Event listing management: publish, update, archive
- Community listing management for local tango schools, venues, and organisations
- Dashboard overview with event status and listing visibility controls
- Role-aware UI — organiser views and permissions differ from standard dancer accounts, handled entirely on the frontend via conditional rendering and route guards
3. Map & Geo-Location Feature
Built the geo-aware event exploration interface — one of the platform's core differentiators for travelling dancers:
- Interactive map view rendering tango events as geo-located markers
- Location-based filtering: surface events near a searched city or the user's current location
- Map/list toggle allowing users to switch between spatial and chronological event views
- Integrated geolocation API for "near me" event discovery
- Smooth marker clustering for cities with high event density to prevent UI overload
Technical Constraints
- Vue.js / Nuxt.js — chosen for its SSR capabilities (critical for SEO on a public event discovery platform), its component-driven architecture, and strong performance on content-heavy list views.
- API-driven frontend — all data is consumed from a backend REST API. The frontend has no direct database access; all state management flows through API calls and Vuex/Pinia store.
- Multi-persona UI from a single codebase — the same Nuxt application serves both public-facing dancer discovery views and authenticated organiser management views, requiring careful route guarding, conditional component rendering, and role-based layout switching.
- Map library integration — geo features required integrating a third-party map SDK (e.g. Leaflet or Google Maps) into the Vue component lifecycle, managing reactive marker state without memory leaks or render conflicts.
- Mobile-first responsive design — the web app must be fully usable on mobile browsers alongside the native iOS/Android apps, since many dancers access the platform from their phones at events.
Key Challenges and How They Were Solved
Challenge 1: SSR-Compatible Map Rendering in Nuxt.js
Problem: Map libraries like Leaflet rely on browser APIs (window, document) that don't exist in Node.js during Nuxt's server-side rendering phase. Naively importing a map library causes the entire SSR build to break.
Solution: Loaded the map component dynamically using Nuxt's <client-only> wrapper and Vue's defineAsyncComponent pattern, ensuring the map is only instantiated in the browser. Marker data is fetched server-side and hydrated into the client-rendered map on mount — giving the page fast SSR load times while keeping the interactive map fully functional.
Challenge 2: Real-Time Listing State in the Organiser Dashboard
Problem: Organisers need to see the live status of their event listings (published, draft, pending review) and update them without full page reloads. Standard page-level navigation would create a sluggish, form-heavy experience.
Solution: Built the organiser dashboard as a client-side SPA section within the Nuxt app using nested routes and a dedicated Vuex module for organiser state. Listing updates trigger optimistic UI updates immediately on the frontend, with API confirmation handled asynchronously. Error rollback restores the previous state if the API call fails — keeping the UX snappy without sacrificing data integrity.
Challenge 3: Multi-Type Event Filtering at Scale
Problem: The platform covers a wide range of tango event types (milongas, classes, festivals, concerts, theatre shows, documentaries, exhibitions) across multiple countries and date ranges. Users needed powerful filtering without server round-trips on every filter interaction.
Solution: Implemented a client-side filter and sort layer on top of paginated API results using computed properties in Vue. Filters for event type, date range, and location are applied reactively without re-fetching — only triggering a new API call when the user navigates to a new page or significantly changes the search scope. This keeps the UI responsive while staying within API rate limits.
Challenge 4: Role-Based UI Without Backend-Rendered Views
Problem: The same Nuxt application serves unauthenticated public users, authenticated dancers, and authenticated organisers — each with different navigation, feature access, and layout structures. Managing this on the frontend without leaking organiser UI to regular users required disciplined access control.
Solution: Implemented a Nuxt route middleware layer that checks authentication state and user role on every navigation. Organiser-only routes redirect unauthenticated or unauthorised users to the appropriate fallback. Organiser UI components are conditionally rendered via role-aware composables rather than hard-coded v-if checks scattered across templates — keeping the access logic centralised and easy to audit.
Challenge 5: Geo-Clustering for High-Density Event Markets
Problem: In tango-dense cities like Buenos Aires, London, or Paris, dozens of events can fall within a small geographic radius. Rendering each as an individual map marker creates an unusable, overlapping cluster that obscures the data rather than surfacing it.
Solution: Integrated a marker clustering library (compatible with the chosen map SDK) that dynamically groups nearby markers based on zoom level. Cluster groups display an event count badge and expand into individual markers as the user zooms in. This keeps the map readable at country/city zoom levels while preserving full granularity at street level.
Stack
Layer
Technology
Frontend Framework
Vue.js
Meta-Framework
Nuxt.js (SSR + SPA hybrid)
State Management
Vuex / Pinia
Map / Geo
Leaflet.js or Google Maps SDK (Vue integration)
Routing
Nuxt File-based Router with middleware
API Communication
REST (Axios / Fetch)
Styling
CSS / SCSS (component-scoped)
Auth
JWT-based, managed via Nuxt middleware
Deployment
Static/SSR via Nuxt build
Rendering Strategy
Hybrid SSR + Client-Side SPA via Nuxt.js.
The application uses Nuxt's hybrid rendering capabilities:
- Public discovery pages (event listings, event detail, map view) are server-side rendered — ensuring full SEO indexability for every event page, fast first-paint for public visitors, and correct Open Graph metadata for social sharing.
- Organiser dashboard is rendered client-side only — authenticated, dynamic, and not requiring SEO. Loaded as a protected SPA section within the same Nuxt app, keeping bundle separation clean.
- Map component uses
<client-only>deferred rendering to avoid SSR conflicts with browser-dependent map libraries, with marker data pre-fetched server-side for instant hydration.
This hybrid approach gives the platform the best of both worlds: search-engine visibility for the public event catalog, and a fast, reactive experience for organiser management workflows.
Key Engineering Challenges Solved
- SSR-safe map rendering — resolved Nuxt/Node.js incompatibility with browser-only map libraries using
<client-only>deferred hydration, without sacrificing SEO on surrounding page content. - Role-based frontend access control — built a centralised Nuxt middleware layer enforcing route-level and component-level access rules for three distinct user roles, without leaking UI or data across personas.
- Client-side filter architecture — implemented reactive multi-dimensional filtering (type, date, location) over paginated API data using Vue computed properties, eliminating redundant network requests on filter interactions.
- Organiser dashboard optimistic UI — built immediate-feedback listing management with async API confirmation and error rollback, creating a snappy CMS-like experience within an SSR-primary application.
- Geo-marker clustering — integrated dynamic zoom-aware marker clustering for high-density tango cities, maintaining map readability across all zoom levels without sacrificing event discoverability.
- Multi-persona UX from a single codebase — served three distinct user experiences (public, dancer, organiser) from one Nuxt application through composable-driven conditional rendering and file-based route guards.