← All projects

Personal project · 2025

Laèlle - E-Commerce Clothing Store

A multi-collection online clothing store with a persistent shopping cart, product filtering, and search across the full catalogue.

Watch demoView code ↗
ReactReact RouterContext APIJavaScriptCSS

Overview

Laèlle is a front-end e-commerce store for a clothing brand, built to work through the parts of a real shopping experience: browsing collections, narrowing products down, adding to a basket, and having that basket still be there when you come back.

The catalogue is organised into four collections - Winter and Summer, each split into tops and bottoms, Style Up for bags, belts and sunglasses, and Ready Outfits - with a shared cart and a search that spans everything.

I built it to get properly comfortable with state that outlives a single component. A cart is the clearest case of that: it is touched from every product page, it has to survive navigation, and it has to survive a refresh.

Features

Homepage and navigation

The store opens on a homepage with a persistent navigation bar carrying the four collections alongside search, account and cart icons, so every part of the shop is one click away from wherever the user is.

Homepage and navigation

Login and account

An account page handles both signing in and creating an account, toggling between the two states from a single component rather than duplicating the form across two pages.

Login and account

Search across the catalogue

A single search page queries every collection at once by spreading all product arrays into one list. The match logic splits both the query and each product name into words and requires every search word to be present, so "white top" matches regardless of the order the words are typed in.

Search across the catalogue

Winter collection

Each collection has its own route, with nested routes for sub-categories such as tops and bottoms, so a shopper can move from a collection down to a specific category without losing their place.

Winter collection

Filtering by size and colour

Category pages filter live by size and colour, composed so the two filters narrow results together rather than one replacing the other. Any product in the filtered results can be added to the cart directly from the grid.

Filtering by size and colour

Style Up accessories

A separate section for finishing an outfit — bags, belts and sunglasses — each with its own sub-route, so accessories are browsable in their own right rather than buried inside a clothing collection.

Style Up accessories

Footer and brand details

The footer carries the brand identity along with location, phone, email and social links, so the store's contact details are available from every page.

Footer and brand details

Shopping basket

The basket lists every added item with its image, name and price, a remove button per line, and a running total across everything in it. Cart state lives in a React Context provider wrapping the whole app, so any product page can add to it without prop drilling — and it is written to localStorage on every change, so a basket survives a refresh or a closed tab.

Shopping basket

How it works

Challenges

Duplicate product IDs breaking cart removal

Removing one item from the cart sometimes removed a second, unrelated item. The cause was in the data rather than the cart logic: each collection's data file numbered its products from 1, so a winter top and a summer top could both carry id 1. Since removal filtered the cart by id, it matched every item sharing that number. The fix was to make IDs unique across the whole catalogue rather than unique within a file - a good reminder that an identifier is only an identifier relative to the set it lives in.

Making search behave the way people actually type

A naive substring search fails as soon as someone types words in a different order to the product name. Splitting both the query and the product name into words, then requiring every query word to appear, made the search tolerant of word order without pulling in a search library for a catalogue this size.

← Back to all projects