Full-scale auto marketplace for Central Asia
The client wanted to build the go-to platform for buying cars in their region. Not a classifieds board — a proper marketplace with new car catalogs, used car listings, rental options, and a dealer directory, all under one roof. The market had a few incumbents, but they were slow, cluttered, and mobile-hostile. The brief was straightforward: build something fast, modern, and SEO-ready from day one, with room to grow into a multi-sided platform.
catalog, filters, and url state
The core product is a consumer-facing catalog that spans three verticals — new cars, used cars, and rentals — each with its own data structure but sharing a unified search experience. The data model behind the search is a four-level hierarchy: brand, model, generation, configuration. Every listing ties back to a specific configuration, which carries the specs — engine type, horsepower, transmission, body style, fuel economy. The multi-parameter filter system lets users drill down across all of these simultaneously, combining brand with price range, mileage, year, and a dozen other attributes. We built the filter state with Zustand to keep it fast and URL-synced — every filter combination produces a shareable, indexable URL, which matters enormously for SEO.
making search fast
Getting search performance right took real effort. The database holds thousands of listings, each with deep relational data — a single car card on the results page touches the listing, its configuration, the configuration's generation, the generation's model, and the model's brand. Naive querying would mean five joins per card, multiplied by thirty cards per page. We leaned on Prisma's relation loading strategy carefully, batched where possible, and added composite indexes on the most common filter combinations. Server components in Next.js 15 handle the initial render, so the first paint comes back fast with full HTML — critical for both perceived speed and search engine crawling.
Beyond search, we built a comparison tool that lets users stack up to four cars side by side, pulling every spec from the configuration level and highlighting differences. A favorites system stores selections per user (or in local storage for anonymous visitors), and a news section with editorially managed content rounds out the consumer experience. The news section also serves an SEO purpose — long-tail keyword pages that funnel organic traffic into the catalog.
multi-subdomain architecture
The multi-subdomain architecture was a deliberate choice from the start. Next.js middleware inspects the incoming hostname and routes to the correct application context — the aggregator (public marketplace), dealer portal, admin panel, and others each live on their own subdomain but share a single codebase and database. This keeps deployment simple while giving each audience a tailored experience. Framer Motion handles transitions and micro-interactions on the consumer side, keeping the UI feeling responsive without overwhelming the page with JavaScript.
seo vs dynamic pages
The honest challenge was balancing SEO requirements against dynamic content. Filter pages need to be indexable, but you can't generate static pages for every possible filter combination — the permutations are astronomical. We settled on a hybrid: the most popular filter combinations (by brand, by city, by body type) get statically generated and revalidated, while deeper filter paths render server-side on demand with proper canonical tags to avoid duplicate content penalties. It's a pragmatic tradeoff, and it works — organic traffic became the primary acquisition channel within the first few months of launch.
what we learned
The takeaway from this build: a marketplace is not a listing page with a database behind it. The real product is the search experience — how fast results load, how intuitive filters feel, how well pages rank. Getting the data model right at the configuration level paid off everywhere downstream, from search to comparison to SEO. If we had cut corners on that hierarchy early on, every feature built on top would have been a workaround.
Stack
Frontend: Next.js 15, React 19, Tailwind CSS, Framer Motion, Zustand
Data fetching: TanStack Query
Backend: Next.js Route Handlers, Prisma 6, PostgreSQL
Architecture: Multi-subdomain via Next.js middleware

