I wanted to share a working example of how to implement range slider functionality similar to Plotly’s built-in range slider using Dash Mantine Components (DMC) LineChart, Sparkline, and RangeSlider components. This approach provides a clean, modern UI while maintaining the familiar zoom/pan functionality users expect.
The Challenge
While Plotly’s native graphs offer excellent range slider functionality out of the box, I wanted to explore creating similar functionality using Dash Mantine Components for projects that are already heavily invested in the Mantine design system. The goal was to create a responsive, interactive chart with:
- A main detailed chart view
- An overview sparkline showing the full dataset
- A range slider for zooming into specific time periods
- Visual feedback showing the selected range
The Solution
I’ve created a working example that combines three DMC components to achieve this:
- DMC LineChart - For the main detailed view dmc.Linechart
- DMC Sparkline - For the overview/context view dmc.Sparkline
- DMC RangeSlider - For selecting the zoom range dmc.Slider
Live Demo Screenshots
Complete Working Code
import dash
from dash import dcc, html, Input, Output, State, callback
import dash_mantine_components as dmc
import pandas as pd
import numpy as np
# Load the Apple stock data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
df.columns = [col.replace("AAPL.", "") for col in df.columns]
# Convert date to datetime and sort
df['Date'] = pd.to_datetime(df['Date'])
df = df.sort_values('Date')
# Sample the data for better performance (take every 5th point)
df_sampled = df.iloc[::5].reset_index(drop=True)
# Prepare data for the charts
chart_data = []
for idx, row in df_sampled.iterrows():
chart_data.append({
'date': row['Date'].strftime('%Y-%m-%d'),
'price': row['Close'],
'index': idx
})
# Extract just the prices for sparkline
sparkline_data = [d['price'] for d in chart_data]
# Initialize the Dash app
app = dash.Dash(__name__)
# Create JavaScript file in assets folder for custom functions
import os
assets_dir = 'assets'
if not os.path.exists(assets_dir):
os.makedirs(assets_dir)
with open(os.path.join(assets_dir, 'dashMantineFunctions.js'), 'w') as f:
f.write("""
window.dashMantineFunctions = {
formatDollar: (value) => {
return '$' + value.toFixed(2);
}
};
""")
# Define the app layout
app.layout = dmc.MantineProvider(
[
dmc.Container(
[
dmc.Center(
dmc.Card(
[
dmc.CardSection(
[
dmc.Text(
"Apple Stock Price with Range Zoom",
size="lg",
fw=500,
ta="center",
mb="md"
),
# Detailed View Label
dmc.Text("Detailed View", size="sm", c="dimmed", mb="xs"),
# Chart container with overlay controls
dmc.Box(
[
# Main LineChart
dmc.LineChart(
id="main-linechart",
h=400,
data=chart_data,
dataKey="date",
series=[
{"name": "price", "color": "blue.6", "label": "Stock Price ($)"}
],
curveType="linear",
withDots=False,
strokeWidth=2,
gridAxis="xy",
withLegend=False,
tooltipAnimationDuration=200,
valueFormatter={"function": "formatDollar"},
style={"paddingBottom": "80px"}
),
# Overlay container for sparkline and slider
dmc.Box(
[
# Sparkline container
dmc.Box(
id="sparkline-container",
children=[
dmc.Sparkline(
id="overview-sparkline",
data=sparkline_data,
h=50,
color="blue.6",
fillOpacity=0.2,
strokeWidth=1,
curveType="linear"
)
],
style={
"position": "relative",
"width": "100%",
"backgroundColor": "rgba(248, 249, 250, 0.95)",
"borderRadius": "4px",
"padding": "5px 0"
}
),
# Range slider directly below sparkline
dmc.RangeSlider(
id="zoom-slider",
value=[0, 100],
min=0,
max=100,
step=1,
minRange=5,
marks=[
{"value": 0, "label": "0%"},
{"value": 25, "label": "25%"},
{"value": 50, "label": "50%"},
{"value": 75, "label": "75%"},
{"value": 100, "label": "100%"}
],
mb=30,
labelAlwaysOn=False,
updatemode="mouseup",
styles={
"track": {"backgroundColor": "#e9ecef"},
"bar": {"backgroundColor": "#339af0"},
"markLabel": {"fontSize": "11px"}
}
),
],
style={
"position": "absolute",
"bottom": "28%",
"left": "8%",
"right": "4%",
"width": "92%",
"backgroundColor": "rgba(255, 255, 255, 0.95)",
"borderRadius": "4px",
"padding": "5px 10px 0 10px",
"boxShadow": "0 -2px 4px rgba(0,0,0,0.05)"
}
)
],
style={
"position": "relative",
"width": "100%",
}
),
# Display selected range info
dmc.Center(
dmc.Text(
id="range-info",
size="sm",
c="dimmed",
ta="center",
mt="-20%"
)
)
],
p="xl"
)
],
shadow="sm",
radius="md",
withBorder=True,
style={
"width": "100%",
"maxWidth": "800px",
"margin": "auto"
}
),
style={
"minHeight": "100vh",
"paddingTop": "50px",
"paddingBottom": "50px"
}
)
],
fluid=True,
style={"backgroundColor": "#f8f9fa"}
)
]
)
# Callback to update the LineChart based on slider range
@callback(
[Output("main-linechart", "data"),
Output("range-info", "children"),
Output("sparkline-container", "children")],
[Input("zoom-slider", "value")]
)
def update_chart_zoom(slider_value):
# Calculate the data indices based on slider percentage
total_points = len(chart_data)
start_idx = int((slider_value[0] / 100) * total_points)
end_idx = int((slider_value[1] / 100) * total_points)
# Ensure we have at least some data points
if end_idx <= start_idx:
end_idx = start_idx + 1
# Slice the data
zoomed_data = chart_data[start_idx:end_idx]
# Create range info text
if zoomed_data:
start_date = zoomed_data[0]['date']
end_date = zoomed_data[-1]['date']
range_text = f"Showing: {start_date} to {end_date} ({len(zoomed_data)} data points)"
else:
range_text = "No data in selected range"
# Create sparkline with overlay to show selected range
sparkline_with_overlay = [
dmc.Sparkline(
data=sparkline_data,
h=50,
color="gray.4",
fillOpacity=0.1,
strokeWidth=1,
curveType="linear"
),
# Overlay showing selected range
html.Div(
style={
"position": "absolute",
"top": "5px",
"left": f"{slider_value[0]}%",
"width": f"{slider_value[1] - slider_value[0]}%",
"height": "50px",
"backgroundColor": "rgba(33, 150, 243, 0.15)",
"border": "1px solid rgba(33, 150, 243, 0.4)",
"borderTop": "none",
"borderBottom": "none",
"pointerEvents": "none"
}
)
]
return zoomed_data, range_text, sparkline_with_overlay
if __name__ == '__main__':
app.run(debug=True, port=8050)
Requirements
pip install dash dash-mantine-components pandas numpy
Running the Example
- Save the code to a file (e.g.,
dmc_range_slider.py) - Run:
python dmc_range_slider.py - Open browser to
http://localhost:8050
I hope this example helps others looking to implement range slider functionality with Dash Mantine Components! Feel free to adapt and improve upon this approach. Looking forward to your feedback and suggestions! ![]()
Follow me on github: pip-install-python (Pip Install Python) · GitHub
check out some of my custom components: https://pip-install-python.com/