Product Comparison System Development
Product comparison — function simplifying choice in high cognitive-load categories: electronics, appliances, auto parts, building materials. User adds several items and sees characteristics in unified table view. Implementation seems simple until you face heterogeneous attributes, nested categories, and performance requirements.
Architectural Solutions
First question: server or browser storage?
LocalStorage / SessionStorage: no account binding, instant write, works without auth. Limitation — no cross-device data. Sufficient for most B2C stores.
Server Session (Redis): list bound to session, persists across navigation, syncs between tabs. Requires auth or anonym session token.
DB with user binding: permanent storage, comparison history. Excessive for most tasks, justified in B2B cabinets.
Optimal scheme: LocalStorage + merge with server list on auth (like cart).
Attribute Data Model
Main complexity — different products have different attributes. TV has "diagonal", fridge has "chamber volume", both can end up in comparison table if user accidentally added different categories.
Attribute schema:
attributes (
id, name, unit, type, -- type: numeric | text | boolean | range
category_id, -- attribute belongs to category
comparable, -- show in comparison
highlight_if_best -- highlight best value
)
product_attributes (
product_id, attribute_id, value_numeric, value_text, value_boolean
)
comparable flag excludes attributes like "sku" or "country" which don't help selection.
Best Value Logic
Highlighting best value — important UX detail: user sees immediately which product has more RAM or lower power consumption. Requires attribute semantics:
type AttributeComparison = {
direction: 'higher_is_better' | 'lower_is_better' | 'none';
};
function highlightBest(values: number[], direction: AttributeComparison['direction']): number {
if (direction === 'higher_is_better') return Math.max(...values);
if (direction === 'lower_is_better') return Math.min(...values);
return NaN; // don't highlight
}
This highlight_direction stored in attributes table. For "color" or "material" attributes, highlighting doesn't apply.
Comparison Table Display
Classic: rows — attributes, columns — products. But 20+ attributes need grouping:
General Specs
├── Brand
├── Country
└── Warranty
Display
├── Diagonal
├── Resolution
└── Refresh Rate
Performance
├── Processor
├── RAM
└── Storage
Groups collapse/expand. Additional filter — "Show only differences" — hides rows with same value across all products. Key feature: in 50-row table, often 30 match.
function filterDifferences(rows: ComparisonRow[]): ComparisonRow[] {
return rows.filter(row => {
const values = row.products.map(p => p.value);
return new Set(values).size > 1; // keep only different rows
});
}
Sticky Header and Horizontal Scroll
With 4–5 products, table overflows viewport. Solution:
- Horizontal scroll container, left column (attribute names) — position: sticky; left: 0
- Header with photos/names — position: sticky; top: 0 (or fixed on scroll down)
- Mobile: horizontal swipe via
overflow-x: autowithscroll-snap-type: x mandatory
CSS trick for sticky header with horizontal scroll — both sticky apply to different elements. Header sticky in parent scroll container, left column sticky in same container.
Limit Products in Comparison
Recommended limit: 3–5 products. More — table unreadable. On 6th add, show notice: "Remove one to add new". UX pattern: instead of block, offer replace existing.
"Add to comparison" button on product card toggles state: added/removed. State synced globally (Zustand, Redux Toolkit) — adding on catalog page shows "added" on product page too.
Floating Comparison Panel
While user browses catalog adding products, bottom/side fixed panel shows current comparison list with "Compare" button. Implementation: fixed-positioned component, appears when list non-empty, CSS transform translateY animation.
Catalog Integration
On category page, each product — checkbox or "compare" icon. With 2+ checked — "Compare selected" CTA appears. Conversion pattern: user doesn't leave page until selecting 2+ candidates.
Sharing and Deep Links
Comparison page URL contains products: /compare?ids=42,117,203. Allows:
- Share link (B2B — send colleague)
- Return via browser history
- Index popular comparisons (noindex long tails)
Timeline
- Basic comparison (LocalStorage, static table, card button): 1–2 weeks
- Full system (attribute grouping, highlight best, sticky header, floating panel, deep links): 2–4 weeks
- Integration with existing catalog on non-standard data model adds 1–2 weeks
Comparison pays in categories where average check high and user spends time choosing — tech, tools, equipment. In simple assortment stores, priority lower.







