Skip to content

Filtering navigated away from the list, so people lost their place and stopped filtering

A compact shopping app built around one interaction decision: brand, category and filter selection happen in modals over the list, so the browse position survives and people actually narrow their search.

19:42
Coats & jacketsWomen · 128 products · new in first
FilterBrand Category New in
Aldery24Norrow21Pellow19Selwyn17Quillon15Oonagh13Kestle11Marram8
Showing 41–56 of 128Place kept while filtering
41AlderyWool-blend wrap coat£18942NorrowQuilted liner jacket£9843PellowCar coat£14544SelwynCotton trench£16545QuillonPadded gilet£7246AlderyDouble-faced coat£22947OonaghBouclé jacket£11848KestleWaxed field jacket£13549NorrowRecycled-fill puffer£12050MarramPackable rain parka£8851SelwynBelted mac£14952PellowPeacoat£15953QuillonCropped puffer£76£9554AlderyCape coat£21055OonaghCord jacket£8456NorrowCheck shacket£69
ShopSaved2BagAccount

Who, what, and how long

Industry

E-commerce & Retail

Duration

7 weeks

Cooperation model

Fixed price

Services
Mobile engineeringInteraction design
Integrations
Product catalogue APIPayment providerPush notificationsAnalytics
Technologies
React NativeExpoTypeScriptExpo RouterREST API
Team
1 Project lead1 Mobile engineer1 Product designer

Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.

Introduction

What we were brought in to do

The brief called for a small, fast shopping app, not a wrapper around the retailer's site. We built it: browse, product detail, bag, favorites and account, with every narrowing action in a modal over the list.

A retailer wanting a small, fast shopping app, not a wrapper around their site. The previous app's filters pushed a new screen and returned to the top of a freshly loaded list, so anyone forty products into a scroll lost all of it. They learned that after doing it twice and stopped filtering, which showed up in the analytics as disinterest in a well-designed feature.

Mobile Engineering

Where the old way broke

  • In their previous app, tapping Filter pushed a new screen. Applying it popped back to the top of a freshly loaded list. Someone forty products into a scroll lost all of it, learned that after doing it twice, and stopped using filters. So they saw fewer relevant products and bought less.

    Nobody reported it. Users don't complain about a feature that costs them their place; they simply stop opening it, and the resulting metric (low filter engagement) reads as a design or discovery problem when it's really a penalty. The team had spent two releases redesigning the filter sheet on that reading.

    Filters, brand and category are modals presented over the list. The list stays mounted underneath, so applying a filter narrows what's already there, and dismissing without applying costs nothing.

01

Filters are modals

Narrowing happens over the list, so the browse position and loaded pages survive.

Filters, brand and category are presented as modals over the list, so the list stays mounted with its loaded pages and scroll position intact. Applying a filter narrows what's already on screen. The previous navigation-based version threw away twenty loaded products and a minute of browsing every time someone opened the filter sheet.

What shipped
  • Presented over the list; the list stays mounted
  • Loaded pages and scroll position survive a filter change
  • Narrows what's on screen, in place
19:42
Coats & jacketsWomen · 128 products · new in first
FilterBrand Category New in
Aldery24Norrow21Pellow19Selwyn17Quillon15Oonagh13Kestle11Marram8
Showing 41–56 of 128Place kept while filtering
41AlderyWool-blend wrap coat£18942NorrowQuilted liner jacket£9843PellowCar coat£14544SelwynCotton trench£16545QuillonPadded gilet£7246AlderyDouble-faced coat£22947OonaghBouclé jacket£11848KestleWaxed field jacket£13549NorrowRecycled-fill puffer£12050MarramPackable rain parka£8851SelwynBelted mac£14952PellowPeacoat£15953QuillonCropped puffer£76£9554AlderyCape coat£21055OonaghCord jacket£8456NorrowCheck shacket£69
ShopSaved2BagAccount
FilterCoats & jackets · 128 products Close
Brand2 chosen
Aldery24Norrow21Pellow19Selwyn17Quillon15Oonagh13Kestle11Marram8
Size · UKAny
6810121416
ColourAny
The list stays open underneath, at item 41
ClearShow 45 products
On screen

The filters sheet raised over the list as a modal: two brands chosen for 45 products, and the browse list still there underneath at item 41.

19:42
Coats & jacketsWomen · 128 products · new in first
FilterBrand Category New in
Aldery24Norrow21Pellow19Selwyn17Quillon15Oonagh13Kestle11Marram8
Showing 41–56 of 128Place kept while filtering
41AlderyWool-blend wrap coat£18942NorrowQuilted liner jacket£9843PellowCar coat£14544SelwynCotton trench£16545QuillonPadded gilet£7246AlderyDouble-faced coat£22947OonaghBouclé jacket£11848KestleWaxed field jacket£13549NorrowRecycled-fill puffer£12050MarramPackable rain parka£8851SelwynBelted mac£14952PellowPeacoat£15953QuillonCropped puffer£76£9554AlderyCape coat£21055OonaghCord jacket£8456NorrowCheck shacket£69
ShopSaved2BagAccount
Brand8 brands · 128 products · drag down to close
Aldery24
Norrow21
Pellow19
Selwyn17
Quillon15
Oonagh13
Kestle11
Marram8
Close without choosingNothing reloads · still at item 41 of 128
On screen

The brand modal being closed without a choice: eight brands with their counts, and nothing reloads, so the list behind it is still at item 41 of 128.

02

Dismissal is free

Opening filters just to look costs nothing, which is why people open them.

Dismissing without applying costs nothing (no reload, no lost position, no re-fetch), and that property decides whether people open filters at all. When looking is expensive, nobody looks; the filter set had been well designed and almost unused for exactly that reason.

What shipped
  • Dismiss without applying is free: no reload or refetch
  • Opening filters to look costs nothing
  • Turned a well-designed, unused feature into a used one
03

Products are addressable

The detail route takes an id, so a shared link opens the product itself.

The product detail route takes an id, so every product has a real address that opens directly from a shared link, a notification or a deep link out of search. Sharing used to land people on the home screen, so a customer sending a friend a product was really sending them a shop and a hunt.

What shipped
  • Detail route keyed by product id: every product addressable
  • Shared links and notifications open the product itself
  • No more landing on the home screen from a share
19:42
Opened from Priya's link
product/40417Link opens this coat
AlderyWool-blend wrap coat£189.00
Colour · Camel
Size · UK 10 Size guide
6810121416
70% wool, 30% recycled polyamide · dry clean
Add to bag · £189.00
On screen

Product detail reached by id from a shared link, so the link opens the wrap coat itself, with its colors, UK sizes and add to bag.

19:42
Saved6 favourites · newest first
All 6Reduced 1In bag 1
AlderyCape coat£210.00OonaghBouclé jacket£118.00In bagNorrowMerino roll-neck£65.00QuillonCropped puffer£76.00£95.00SelwynBelted mac£149.00MarramPackable rain parka£88.00
ShopSaved2BagAccount
04

Favourites is a tab

On screen

Saved items on their own tab, six favorites with one reduced and one already in the bag, because opening favorites is a different intent from narrowing a browse.

A different intent gets a different place of its own.

Favorites is a tab, not a filter, because saving something and narrowing a list are different intents, and folding one into the other makes both harder to reach. A tab also gives favorites its own state and its own empty state, which is where the prompt to start saving actually belongs.

What shipped
  • A different intent gets its own place, never a filter value
  • Its own state and its own empty state
  • The prompt to start saving lands where it belongs
05

Small on purpose

Five surfaces. A shopping app that takes a while to open is a shopping app people don't open.

Five surfaces, deliberately. Every extra screen is startup cost, bundle size and one more decision for the customer, and a shopping app that takes a moment to open is one people stop opening. Cuts were judged on that basis, whatever a feature's individual merits.

What shipped
  • Five surfaces total, sized against startup cost
  • Cuts made on startup cost, not individual merit
  • An app that opens slowly is an app that goes unopened
19:42
EMEllen MarshMember since 2024 · UK 10 · S
OrdersAll orders
SR-81446On its wayDispatched · arrives Fri 18 Sep£118.0012 Sep · 1 item
SR-80912DeliveredDelivered 5 Sep£153.003 Sep · 2 items
SR-79308RefundedReturned · refunded 28 Aug£72.0021 Aug · 1 item
SR-78175DeliveredDelivered 5 Aug£211.002 Aug · 3 items
Settings
AddressesHome · Leith Walk, Edinburgh
PaymentVisa ending 4471
SizesCoats UK 10 · knitwear S
NotificationsPrice drops on saved items
ShopSaved2BagAccount
On screen

The account tab: four orders with their status and the settings behind them. With browse, product detail, the bag and saved, it's the fifth and last surface in the app.

What we built together

Presented every narrowing action as a modal over the list, never a pushed screen

The presentation decision came first because it determines how browse state is held, and everything else in the app follows from that.

Kept the list mounted underneath, so applying a filter never reloads from the top

Applying narrows what's already on screen in place, the difference between refining a browse and starting a new one.

Made dismissal free, so opening filters just to look isn't a decision

Dismissing without applying costs nothing (no reload, no lost position), which makes opening filters just to look a free action.

Kept favorites on its own tab, separate from filters, because it's a different intent

Favorites is a tab with its own state and its own empty state, kept out of the filter set, because saving and narrowing are different intents.

Built the product route to accept an id directly, so a shared link opens the product

The detail route takes a product id, so a shared link or a notification opens the product itself.

Operational results after launch

0

List reloads on filter

Never

Scroll position lost

None

Cost of opening filters

5

Surfaces in the app

List reloads on filter is zero: the list stays mounted. Scroll position lost is never. Cost of opening filters is none, the property that decides whether people open them. Surfaces in the app is a count, and a deliberate one.

Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.

Process

Phase by phase

  1. Phase 1: Browse

    The List That Stays

    Built the product list as the app's home surface, holding its scroll position and loaded pages across every interaction short of a navigation.

    • Product List
    • Paging
    • Scroll Retention
  2. Phase 2: The Modals

    Narrowing In Place

    Built brand, category and filter selection as modals over the list. The list is never remounted, so applying a filter narrows it in place.

    • Filter Modal
    • Brand Modal
    • Category Modal
  3. Phase 3: Product And Bag

    Detail Without Losing Browse

    Built product detail as a route by id, so it can be linked to directly, and the bag as a tab: the two places where a push is the right answer.

    • Product Detail
    • Bag
    • Deep Links
  4. Phase 4: Favourites

    A Tab, Not A Filter

    Kept favorites as its own tab. It looks like a filter and isn't: someone opening favorites has a different intent from someone narrowing a browse.

    • Favourites
    • Sync
    • Empty State
19:42
Bag2 items · prices include VAT
AlderyWool-blend wrap coatCamel · UK 101£189.00
NorrowMerino roll-neckEcru · S1£65.00
AUTUMN20 applied£20 off orders over £200
Items (2)£254.00
Promotion−£20.00
Standard deliveryFree
Total£234.00
Includes VAT at 20%£39.00
Checkout · £234.00
ShopSaved2BagAccount
On screen

The bag: two lines, a £20 promotion applied, free delivery, and a £234.00 total shown with the £39.00 of VAT it already includes.

Ways of working

About our collaboration

They brought session recordings instead of a brief. Watching three people filter once, lose their place and never filter again settled the design question in about ten minutes and made the rest of the engagement straightforward.

Seven weeks, fixed, with the presentation decision made first because it determines everything about how browse state is held. Scope was actively defended: every extra surface proposed was weighed against startup cost, on the grounds that a shopping app that takes a moment to open is one people stop opening.

What we'd carry into the next one

A pushed screen unmounts what's behind it. For anything that narrows a list, that's the wrong presentation.

A pushed screen unmounts what's behind it. That's right for a destination and wrong for anything whose job is to narrow what you're already looking at.

Users don't tell you they stopped using a feature. They just stop, and the analytics look like disinterest.

People don't report abandoning a feature, so the metric shows disinterest, and two releases had been spent redesigning a sheet that was never the problem.

Make exploratory actions free to abandon, or people won't start them.

Exploration only happens when abandoning is free. The moment looking has a cost, users stop looking and the feature's usage collapses.

Favorites is an intent, not a filter, and folding it into the filter set hides it from the people who want it.

Folding favorites into the filter set hides an intent inside a mechanism, and the people who wanted it were the least likely to look for it there.

Forty products in, then a filter

The list never reloads, because it never leaves the screen.

One browse session in four steps: scrolled to item 41, filters opened as a modal, a brand filter applied, and the sheet opened again and dismissed. Keep an eye on the reload counter and the scroll position, and on what the previous app did at the same moment. Switch steps, or use the arrow keys once a step is focused.

One browse session · coats & jackets, 128 products

Products and list positions after filtering are illustrative
List reloads0across all four steps
Scroll positionKeptItem 41 of 128
Products shown128no filter applied
Coats & jackets128 products Filter
Showing 41–56 of 128
  1. 41Wool-blend wrap coatAldery£189.00
  2. 42Quilted liner jacketNorrow£98.00
  3. 43Car coatPellow£145.00
  4. 44Cotton trenchSelwyn£165.00
  5. 45Padded giletQuillon£72.00
  6. 46Double-faced coatAldery£229.00
Stayrail

The list is the home surface. It holds its scroll position and its loaded pages across every interaction short of a navigation.

The previous app, same moment

The same starting point in both apps: a shopper forty products into a scroll.

  1. Step 1 · reloads 0
  2. Step 2 · reloads 0
  3. Step 3 · reloads 0
  4. Step 4 · reloads 0
Architecture

A presentation decision, made first, that decides how browse state is held

A pushed screen unmounts what's behind it, which is right for a destination and wrong for anything that narrows what you're already looking at. So narrowing is a modal, and the one real destination, the product, is a route by id.

  1. 01 · Trigger
    A tap on Filter, Brand or Category, or a shared linkEvery narrowing action is presented as a modal. A shared link, a notification or a deep link out of search carries a product id.
  2. 02 · Routing
    Expo Router: modals and a product route by idFilter, brand and category are modals, never pushed screens. Product detail takes an id, so every product has a real address.
  3. 03 · Narrowing
    Narrow what is already on screenApplying a filter narrows the mounted list instead of replacing it: 0 list reloads. Dismissing without applying re-fetches nothing.
  4. 04 · State
    The list that stays mountedLoaded pages and scroll position survive every interaction short of a navigation. Favorites keeps its own state on its own tab.
  5. 05 · Client
    React Native app, 5 surfacesBrowse, Product detail, Bag, Saved, Account. Built with Expo and TypeScript over a REST API, sized against startup cost.

What a shopping app must never break

Browse position, shared links & the bag total

Nobody loses their place

Filters, brand and category are modals over a list that stays mounted, so applying one causes 0 list reloads, the scroll position is never lost, and closing without applying costs nothing. Opening filters to look is no longer a gamble.

A shared link opens the product

The product detail route takes an id, so a link sent to a friend, a notification or a deep link out of search opens that product, with no home screen and no hunt.

The bag shows its working

Each line, the promotion as its own line, delivery, and the total with the tax already inside it: £234.00 including £39.00 VAT, shown as already included rather than left for the shopper to work out.

Shoppers losing their place every time they filter? Scope your build in three minutes.

Scope your build
Have a project?

Let's talk

Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.