Blog · 25 mai 2026 · Engineering

Smaller images, smarter filters, smoother feel

This sprint we rebuilt parts of the marketplace you touch every day — and a few you'll never notice. Here's how.

Two months ago, searching the marketplace for something common — a bike, say — was a guessing game. The sidebar showed every category we have, top-to-bottom, in a tree. You'd pick one, scroll, hit a dead end, back-arrow, try the next, repeat. The sidebar described our catalogue, not your results.

That bothered us. So we tore the sidebar apart and rebuilt it. Along the way we shaved the bytes off every photo on the site, made the header step out of your way while you scroll, and threw in a handful of small browser tricks that ladder up to "something just feels different here." This is the rundown.

−30%
Image size
At equal visual quality, on every photo on the site.
One click
Refine your search
Picking a category from the matching set, not the catalogue.
0 px
Modal jitter
Opening a modal no longer shifts the page.

Faster images

Every image goes through us. Users upload a 4 MB JPEG of their kitchen table; we resize it for product cards, product pages and thumbnails, and serve those copies forever. The pipeline was set up years ago and it worked, but it had two problems we'd been ignoring.

Problem one: we were keeping each image in the format it was uploaded in. JPEG stayed JPEG, PNG stayed PNG. We resized aggressively but we'd never re-encoded into a smaller modern format — leaving roughly a third of every photo's bytes on the table.

Problem two: the browser had no idea which image on a product page was the most important. The big hero image, the four thumbnails next to it, the recommended-products row below — all loaded with the same priority, racing each other for the same connection. The hero showed up when it felt like it.

Both fixable.

One format for every photo

We standardised on a single modern image format on the way in. Whatever format you upload — JPEG, PNG, animated GIF — we re-encode to the smaller one. For animated GIFs we keep the first frame; product photos aren't animations, and we'd rather have a smaller still than a chunky loop nobody asked for.

Same visual quality. About 30% fewer bytes per image. Applied to every new upload from day one of the sprint.

Telling the browser what matters

This was the bigger win. Browsers can be told which image on a page is the most important — the "hero" — so they fetch it before everything else. We added these hints in three places:

  • The hero on a product page: highest priority. Side thumbs explicitly lower so they don't compete.
  • The first row of cards on the home and search feeds: high. Rows below the fold wait until you scroll towards them.
  • Decorative imagery (the small category icons, etc.): low, and only loaded on demand.
High
Low
Low
Low
Low

The hero downloads first; thumbnails wait their turn.

One quiet bug we caught along the way

The product page gallery was picking a photo size based on whatever order the server happened to return them in, not on what size it actually needed. Sometimes the hero was a 300-pixel thumbnail; sometimes a thumbnail was the full-resolution version. A fresh display "feels" sharper now because the right size ends up in the right slot.

Smarter search filtering

Back to the bike example. A search like that crosses many categories — adult bikes, kids' bikes, parts, accessories — and the old sidebar had no idea which of them actually had hits. You found out by clicking each one. The new sidebar just shows the matching set, with counts.

Category
Adult bikes
(64)
Kids' bikes
(22)
Bike parts
(41)
Accessories
(18)
Brand
Btwin
(31)
Trek
(14)
Specialized
(9)
Cube
(7)
Price
€120€2 400

The same idea unfolds for every attribute we know about. Brand, colour, size, frame size, weight — if the matching results have values for it, that attribute appears in the sidebar with checkboxes and counts.

For numerical attributes (price, weight, capacity, etc.) we replaced the old "enter a min and a max" inputs with a slider whose bounds match the actual range present in the results. If the cheapest matching listing is €120, the slider starts at €120. No more typing a min of €10 when no result is cheaper than €120 anyway.

Old sidebar

Static category tree (every node in the catalogue). Empty min/max inputs for price. No idea which categories actually have results until you clicked through them.

New sidebar

Only categories with matching results, sorted by count. Multi-select to widen. Slider bounded by the real min/max of matching listings. The sidebar is a map of the result set, not a directory of the catalogue.

When the result set is empty

One more wrinkle: if you over-filter and land on zero results, the entire sidebar disappears. There's nothing to refine — filter chips and checkboxes would just be visual noise. The page collapses to a single "Clear all filters" button, centred. One click and you're back where the sidebar can help.

A more polished browsing feel

Five small things landed this sprint that together change the feel of the site. None of them are blockbuster features; each is one or two CSS declarations. They're listed in order of how often a typical user will encounter them.

The header steps out of the way

The white navigation bar at the top of every page now slides up off screen when you scroll down, and back into view when you scroll up — the classic "header gets out of my way while I'm reading" pattern. Browsers that don't support the underlying feature simply keep the bar visible; nothing breaks.

Smoother card → product transitions

Click a product card on the home or search feed and its hero image now animates smoothly into place on the product detail page — instead of the old jump-cut. The card's title, price and like button do the same. It's the difference between a page that "loads" and one that "transforms".

Headings wrap more nicely

Product titles, card titles, section headings — none of them now leave a single-word orphan on the last line. The browser balances the line breaks for us. One CSS declaration, applied site-wide, immediately visible everywhere a heading wraps.

No more horizontal jitter when modals open

Before: open the share modal, the page lurches 15 pixels to the right as the scrollbar vanishes, and everything underneath shifts. Now: a reserved gutter holds the space whether the scrollbar is there or not. Modals open without the page lurching.

Faster language switches

Date formatting in your language used to ship bundled with the formatting data for all other supported languages — about 100 KB of locale data, even though you only ever needed one. We now load only the language you're using. Smaller bundle, faster first paint.

Under-the-hood cleanup

A pile of small adjustments that you'd struggle to notice individually but that show up as cleaner traces in our performance dashboards. None of them changed any behaviour you'd see. The headline numbers:

−1 RTT
API handshake
Connection prewarmed before the first request fires.
idle
Analytics init
Off the critical path; runs during browser idle time.
less
Telemetry chatter
We trimmed how often diagnostic data is reported.
audit
Bundle review
We mapped the heavy modules; cuts queued for next sprint.

What we didn't ship (yet)

A few items started but didn't make this sprint:

  • Multi-value attribute filtering. The UI accepts multiple checked values per attribute (you can tick three brands), but only one is honoured server-side today. Next sprint.
  • Smarter facet recomputation. Right now checking "ASUS" removes the other brand options from the list. The polished version re-counts each facet excluding its own filter so options stay visible. Doable; just the more expensive query path.
  • AVIF images. Even smaller than what we ship today. Costs more encoder work on the server side; held back until we benchmark.

That's it for this sprint. Next post should cover the work we started on shared user-to-user listings — quietly the most-requested feature in the last quarter — once it's far enough along to talk about.

All posts
engineeringperformanceproduct