component-filters-price-range Snippet
snippets/component-filters-price-range.liquid renders a dual-range price filter component for collection and search pages. It provides both slider controls and number inputs for selecting minimum and maximum price ranges, with synchronized updates between inputs and visual slider track. The component uses the <price-range> custom element for interactive behavior.
What It Does
- Renders a dual-range price filter with slider and number input controls.
- Synchronizes min/max range inputs with visual slider track.
- Displays currency symbol and formatted price inputs.
- Converts prices from cents (Shopify format) to dollars (display format).
- Integrates with Section Rendering API for AJAX filter updates.
- Provides real-time visual feedback as users adjust price range.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
filter | object | required | Filter object with type: 'price_range' and price range data. |
id_prefix | string | optional | Prefix for input IDs (typically 'Filter-'). |
filter_type | string | optional | Filter type identifier (documented but may not be used). |
Dependencies & Assets
| Type | Files / Components |
|---|---|
| CSS | Inline {% style %} block with comprehensive price range styling |
| JavaScript | component-filters-price-range.js (defines <price-range> custom element) |
| Custom Elements | <price-range> (handles slider synchronization and UI updates) |
| Data | Requires filter object with range_max, min_value, max_value, and cart.currency.symbol |
- Custom element handles all interactive behavior and synchronization.
- Inline styles provide all price range-specific CSS (no external CSS file required).
- Section Rendering API handles filter updates via
data-render-sectionattributes.
Dynamic Styles
The snippet includes extensive inline styles for price range slider, inputs, and layout:
{%- style -%}
.slider-container {
height: 4px;
position: relative;
background: #e4e4e4;
border-radius: 5px;
}
.slider-container .price-slider {
height: 100%;
left: 0%;
right: 0%;
position: absolute;
border-radius: 5px;
background: #000;
display: block;
}
.range-input input {
position: absolute;
width: 100%;
height: 4px;
background: none;
top: 0px;
left: 0px;
pointer-events: none;
cursor: pointer;
-webkit-appearance: none;
z-index: 2;
}
/* ... additional styling for inputs, labels, mobile ... */
{%- endstyle -%}- Slider track: Visual representation of selected price range.
- Dual range inputs: Two overlapping range inputs for min/max selection.
- Number inputs: Text inputs for precise price entry.
- Mobile responsive: Adjustments for smaller screens at 768px breakpoint.
Markup Structure
<price-range>
<div class="price-range-main" currency-symbol="{{ cart.currency.symbol }}">
<div class="price-range-subcontainer">
<div class="range-input">
<!-- Min and max range inputs -->
</div>
<div class="price-input-container">
<div class="slider-container">
<div class="price-slider"></div>
</div>
<div class="price-input">
<!-- Min and max number inputs -->
</div>
</div>
</div>
</div>
</price-range>- Custom element: Uses
<price-range>wrapper for JavaScript functionality. - Currency symbol: Passed via
currency-symbolattribute for display. - Dual controls: Both slider and number inputs for flexible interaction.
Range Inputs (Sliders)
<div class="range-input">
<input
type="range"
class="min-range"
min="0"
max="{{ filter.range_max | divided_by: 100 }}"
{%- if filter.min_value.value -%}
value="{{ filter.min_value.value | divided_by: 100 }}"
{%- else -%}
value="0"
{%- endif -%}
step="1"
name="{{ filter.min_value.param_name }}"
data-render-section
>
<input
type="range"
class="max-range"
min="0"
max="{{ filter.range_max | divided_by: 100 }}"
{%- if filter.max_value.value -%}
value="{{ filter.max_value.value | divided_by: 100 }}"
{%- else -%}
value="{{ filter.range_max | divided_by: 100 }}"
{%- endif -%}
step="1"
name="{{ filter.max_value.param_name }}"
data-render-section
>
</div>- Price conversion: Divides by 100 to convert from cents (Shopify) to dollars (display).
- Min range: Defaults to 0 if no min value set.
- Max range: Defaults to
range_maxif no max value set. - Section Rendering API:
data-render-sectiontriggers AJAX updates. - Overlapping inputs: Both inputs positioned absolutely for dual-range functionality.
Visual Slider Track
<div class="slider-container">
<div class="price-slider"></div>
</div>- Visual indicator: Shows selected price range as filled portion of track.
- Dynamic positioning: JavaScript updates
leftandrightCSS properties based on min/max values.
Number Inputs
<div class="price-input">
<div class="price-field">
<span>From</span>
<label>
<span class="currency-symbol">{{ cart.currency.symbol }}</span>
<input
type="number"
class="min-number"
min="0"
max="{{ filter.range_max | divided_by: 100 }}"
{%- if filter.min_value.value -%}
value="{{ filter.min_value.value | divided_by: 100 }}"
{%- else -%}
value="0"
{%- endif -%}
step="1"
data-render-section
>
</label>
</div>
<div class="price-field">
<span>To</span>
<label>
<span class="currency-symbol">{{ cart.currency.symbol }}</span>
<input
type="number"
class="max-number"
min="0"
max="{{ filter.range_max | divided_by: 100 }}"
{%- if filter.max_value.value -%}
value="{{ filter.max_value.value | divided_by: 100 }}"
{%- else -%}
value="{{ filter.range_max | divided_by: 100 }}"
{%- endif -%}
step="1"
data-render-section
>
</label>
</div>
</div>- Currency display: Shows currency symbol before number input.
- Synchronized values: Values match range inputs (converted from cents).
- Section Rendering API:
data-render-sectiontriggers AJAX updates. - Labels: "From" and "To" labels for clarity.
Behavior
- Dual-range selection: Users can set both minimum and maximum price.
- Synchronized inputs: Range sliders and number inputs stay in sync.
- Visual feedback: Slider track updates to show selected range.
- Price conversion: Automatically converts between cents (Shopify) and dollars (display).
- Real-time updates: Changes trigger Section Rendering API requests.
- Validation: JavaScript prevents invalid states (min > max).
- Currency display: Shows appropriate currency symbol from cart.
Usage Example
{% if f.type == 'price_range' %}
{% render 'component-filters-price-range',
filter: f,
id_prefix: 'Filter-',
filter_type: 'horizontal'
%}
{% endif %}Typically used within:
component-filters-horizontal.liquidcomponent-filters-drawer.liquidcomponent-filters-sidebar.liquid
Implementation Notes
Custom element requirement: Requires
component-filters-price-range.jsto be loaded which defines the<price-range>custom element.Price conversion: Shopify stores prices in cents, but the component displays in dollars:
filter.range_max | divided_by: 100converts max pricefilter.min_value.value | divided_by: 100converts min valuefilter.max_value.value | divided_by: 100converts max value
Dual range inputs: Two overlapping
<input type="range">elements create the dual-range slider:- Min range:
.min-rangeclass - Max range:
.max-rangeclass - Both positioned absolutely in
.range-inputcontainer
- Min range:
Visual slider track: The
.price-sliderelement visually represents the selected range:- JavaScript updates
leftandrightCSS properties left= (min / maxRange) * 100%right= 100 - (max / maxRange) * 100%
- JavaScript updates
Section Rendering API: All inputs use
data-render-sectionattribute (without value) to trigger AJAX updates when values change.Currency symbol: Retrieved from
cart.currency.symboland passed viacurrency-symbolattribute to custom element.Input synchronization: The
<price-range>custom element synchronizes:- Range inputs ↔ Number inputs
- Range inputs ↔ Visual slider track
- Number inputs ↔ Visual slider track
Default values:
- Min defaults to 0 if
filter.min_value.valueis not set - Max defaults to
filter.range_maxiffilter.max_value.valueis not set
- Min defaults to 0 if
Input names: Range inputs use Shopify filter parameter names:
- Min:
filter.min_value.param_name(typicallyfilter.v.price.gte) - Max:
filter.max_value.param_name(typicallyfilter.v.price.lte)
- Min:
Number input styling: Number inputs are styled to look like labels with currency symbol, creating a cohesive design.
Mobile responsive: Styles adjust at 768px breakpoint:
- Price input flex direction changes
- Field margins adjusted
- Layout optimized for smaller screens
WebKit styling: Custom styles for range input thumbs in WebKit browsers:
- Removes default appearance
- Custom thumb size (10px × 10px)
- Black circular thumb
Spin button removal: Number inputs have spin buttons removed via CSS for cleaner appearance.
Z-index layering: Range inputs use
z-index: 2to appear above slider track for interaction.Pointer events: Range inputs use
pointer-events: noneon container,pointer-events: autoon thumbs for proper interaction.Step value: All inputs use
step="1"for whole dollar increments.CSS class dependencies: Snippet relies on CSS classes:
.price-range-main.price-range-subcontainer.range-input.min-range.max-range.price-input-container.slider-container.price-slider.price-input.price-field.min-number.max-number.currency-symbol
JavaScript integration: The
<price-range>custom element:- Reads URL parameters on init
- Binds event listeners to all inputs
- Updates UI in real-time
- Prevents invalid states (min > max)
- Handles synchronization between inputs
URL parameter format: JavaScript reads filter values from URL:
filter.v.price.gtefor minimumfilter.v.price.ltefor maximum
No translation keys: Price range labels ("From", "To") are hardcoded in English. Consider using translation filters if internationalization is needed.
Filter object structure: Requires filter object with:
type: 'price_range'range_max: Maximum price in centsmin_value.value: Current min value in cents (optional)max_value.value: Current max value in cents (optional)min_value.param_name: Parameter name for min (e.g.,filter.v.price.gte)max_value.param_name: Parameter name for max (e.g.,filter.v.price.lte)
Cart currency: Requires
cart.currency.symbolto be available for currency display.Accessibility considerations:
- Proper label associations
- Number inputs for keyboard entry
- Range inputs for mouse/touch interaction
- Semantic HTML structure
Performance: Inline styles prevent additional HTTP request but increase HTML size. Consider extracting to CSS file if used multiple times.
Validation: JavaScript in custom element validates that min ≤ max and adjusts values if needed.
Real-time updates: All input changes trigger immediate UI updates and Section Rendering API requests.
Visual feedback: Slider track provides immediate visual feedback as users adjust range.
Input constraints: All inputs respect min/max constraints based on
filter.range_max.Currency symbol display: Currency symbol is displayed before number inputs for clarity.
Mobile optimization: Layout adjusts for mobile with flex direction changes and spacing adjustments.