Insta Stories

Instagram and Snapchat style stories component with video support, custom renderers, and interactive controls.

dash-insta-stories is a Dash component library that brings Instagram and Snapchat-style stories to your Dash applications. It features automatic progression with customizable intervals, video and image support, custom headers and loaders, interactive navigation with tap/swipe controls, keyboard navigation, story preloading for smooth transitions, custom renderers for complex content, and responsive design with configurable dimensions.

Installation

Visit GitHub Repo

⭐️ Star this component on GitHub! Stay up to date on new releases and browse the codebase.

pip install dash-insta-stories

Quick Start

Display a basic Instagram-style story viewer with automatic progression. Tap the left or right side to navigate between stories.

# File: docs/dash_insta_stories/introduction.py

from dash_insta_stories import DashInstaStories
from dash import *
import dash_mantine_components as dmc

# Define your stories
stories = [
    {
        "url": "/assets/images/02.jpg",
        "type": "image",
        "duration": 5000,
        "header": {
            "heading": "This is kinda like...",
            "subheading": "Snapchat 😜",
            "profileImage": "https://avatars.githubusercontent.com/u/120129682?v=4"
        },
        # "seeMore": lambda: {"url": "https://example.com"},
        "styles": {"background": "#f5f5f5"},
        "preloadResource": True
    },
    {
        "url": "/assets/images/03.jpg",
        "header": {
            "heading": "Mobile First! 📱",
            "subheading": "Design 👀",
            "profileImage": "https://avatars.githubusercontent.com/u/120129682?v=4"
        }
    },
    {
        "url": "/assets/images/04.jpg",
        "header": {
            "heading": "Kinda like a carousel",
            "subheading": "but not really...",
            "profileImage": "https://avatars.githubusercontent.com/u/120129682?v=4"
        }
    }
    # Add more stories as needed
]

component = dmc.Stack([
    dmc.Center(
        DashInstaStories(
            id='input',
            stories=stories,  # Pass the stories to DashInstaStories
            renderers=[],  # Pass the renderers to DashInstaStories
            defaultInterval=2200,  # Pass the defaultInterval to DashInstaStories
            loader=None,  # Pass the loader to DashInstaStories
            header=None,  # Pass the header to DashInstaStories
            storyContainerStyles={},  # Pass the storyContainerStyles to DashInstaStories
            width=360,  # Pass the width to DashInstaStories
            height=640,  # Pass the height to DashInstaStories
            storyStyles={},  # Pass the storyStyles to DashInstaStories
            progressContainerStyles={},  # Pass the progressContainerStyles to DashInstaStories
            progressWrapperStyles={},  # Pass the progressWrapperStyles to DashInstaStories
            progressStyles={},  # Pass the progressStyles to DashInstaStories
            loop=True,  # Pass the loop to DashInstaStories
            isPaused=False,  # Pass the isPaused to DashInstaStories
            currentIndex=None,  # Pass the currentIndex to DashInstaStories
            # onStoryStart=None,  # Pass the onStoryStart to DashInstaStories
            # onStoryEnd=None,  # Pass the onStoryEnd to DashInstaStories
            # onAllStoriesEnd=None,  # Pass the onAllStoriesEnd to DashInstaStories
            # onNext=None,  # Pass the onNext to DashInstaStories
            # onPrevious=None,  # Pass the onPrevious to DashInstaStories
            keyboardNavigation=True,  # Pass the keyboardNavigation to DashInstaStories
            preventDefault=False,  # Pass the preventDefault to DashInstaStories
            preloadCount=1,  # Pass the preloadCount to DashInstaStories
        )
    ),
], style={'width': '100%', 'overflow':'auto'})

Simple Image Stories

Create a story viewer using an array of image URLs. The component automatically handles progression and provides tap controls for navigation.

from dash import Dash
from dash_insta_stories import DashInstaStories

app = Dash(__name__)

stories = [
    'https://picsum.photos/400/600?image=1',
    'https://picsum.photos/400/600?image=2',
    'https://picsum.photos/400/600?image=3',
]

app.layout = DashInstaStories(
    id='simple-stories',
    stories=stories,
    width=360,
    height=640,
    defaultInterval=3000,  # 3 seconds per story
)

Simple Stories Array:

Pass an array of image URLs as strings for quick setup. Each story displays for the defaultInterval duration.


Advanced Story Objects

Use story objects for fine-grained control over each story's appearance and behavior, including headers, custom durations, and video content.

from dash_insta_stories import DashInstaStories

advanced_stories = [
    {
        'url': 'https://picsum.photos/400/600?image=10',
        'duration': 5000,  # 5 seconds
        'header': {
            'heading': 'Product Launch',
            'subheading': '2 hours ago',
            'profileImage': 'https://picsum.photos/100/100?image=20'
        }
    },
    {
        'url': 'https://example.com/video.mp4',
        'type': 'video',
        'duration': 10000,
        'header': {
            'heading': 'Behind the Scenes',
            'subheading': '5 hours ago',
            'profileImage': 'https://picsum.photos/100/100?image=21'
        }
    },
    {
        'url': 'https://picsum.photos/400/600?image=30',
        'duration': 4000,
        'seeMore': True,  # Adds "See More" button
        'header': {
            'heading': 'New Collection',
            'subheading': '1 day ago',
            'profileImage': 'https://picsum.photos/100/100?image=22'
        }
    }
]

app.layout = DashInstaStories(
    id='advanced-stories',
    stories=advanced_stories,
    width='100%',
    height='100vh',
    loop=True,  # Loop back to first story after last
)

Story Object Properties:


Interactive Callbacks

Track story progression and user interactions using Dash callbacks. The component provides callback properties for story events and navigation.

from dash import Dash, html, callback, Input, Output
from dash_insta_stories import DashInstaStories

app = Dash(__name__)

app.layout = html.Div([
    DashInstaStories(
        id='interactive-stories',
        stories=[
            'https://picsum.photos/400/600?image=40',
            'https://picsum.photos/400/600?image=41',
            'https://picsum.photos/400/600?image=42',
        ],
        width=360,
        height=640,
    ),
    html.Div(id='story-info')
])

@callback(
    Output('story-info', 'children'),
    Input('interactive-stories', 'currentIndex'),
    prevent_initial_call=True
)
def display_story_info(current_index):
    if current_index is not None:
        return f'Currently viewing story {current_index + 1}'
    return 'No story selected'

Available Callback Events:


Playback Control

Control story playback programmatically using the isPaused and currentIndex properties.

from dash import Dash, html, callback, Input, Output
import dash_mantine_components as dmc
from dash_insta_stories import DashInstaStories

app = Dash(__name__)

app.layout = html.Div([
    DashInstaStories(
        id='controlled-stories',
        stories=[
            'https://picsum.photos/400/600?image=50',
            'https://picsum.photos/400/600?image=51',
            'https://picsum.photos/400/600?image=52',
        ],
        width=360,
        height=640,
        isPaused=False,
        currentIndex=0,
    ),
    dmc.Group([
        dmc.Button('Pause', id='pause-btn', n_clicks=0),
        dmc.Button('Resume', id='resume-btn', n_clicks=0),
        dmc.Button('Jump to Story 3', id='jump-btn', n_clicks=0),
    ])
])

@callback(
    Output('controlled-stories', 'isPaused'),
    Input('pause-btn', 'n_clicks'),
    Input('resume-btn', 'n_clicks'),
    prevent_initial_call=True
)
def toggle_playback(pause_clicks, resume_clicks):
    ctx = callback_context
    if not ctx.triggered:
        return False

    button_id = ctx.triggered[0]['prop_id'].split('.')[0]
    return button_id == 'pause-btn'

@callback(
    Output('controlled-stories', 'currentIndex'),
    Input('jump-btn', 'n_clicks'),
    prevent_initial_call=True
)
def jump_to_story(n_clicks):
    if n_clicks:
        return 2  # Jump to third story (0-indexed)
    return 0

Styling and Customization

Customize the appearance of stories, progress bars, and containers using style properties.

from dash_insta_stories import DashInstaStories

app.layout = DashInstaStories(
    id='styled-stories',
    stories=[
        'https://picsum.photos/400/600?image=60',
        'https://picsum.photos/400/600?image=61',
    ],
    width=400,
    height=700,
    storyContainerStyles={
        'borderRadius': '20px',
        'overflow': 'hidden',
        'boxShadow': '0 4px 20px rgba(0,0,0,0.3)'
    },
    storyStyles={
        'objectFit': 'cover',
        'filter': 'brightness(0.95)'
    },
    progressContainerStyles={
        'padding': '10px'
    },
    progressStyles={
        'background': 'linear-gradient(to right, #667eea, #764ba2)',
        'height': '3px'
    }
)

Available Style Properties:


Keyboard Navigation

Enable keyboard controls for desktop users to navigate stories using arrow keys.

from dash_insta_stories import DashInstaStories

app.layout = DashInstaStories(
    id='keyboard-stories',
    stories=[
        'https://picsum.photos/400/600?image=70',
        'https://picsum.photos/400/600?image=71',
        'https://picsum.photos/400/600?image=72',
    ],
    width=360,
    height=640,
    keyboardNavigation=True,  # Enable keyboard controls
    preventDefault=False,     # Allow default browser behavior
)

Keyboard Controls:


Performance Optimization

Control story preloading to optimize performance and bandwidth usage.

from dash_insta_stories import DashInstaStories

app.layout = DashInstaStories(
    id='optimized-stories',
    stories=[
        {
            'url': 'https://picsum.photos/400/600?image=80',
            'preloadResource': True  # Preload this story
        },
        {
            'url': 'https://example.com/large-video.mp4',
            'type': 'video',
            'preloadResource': False  # Don't preload video
        },
        {
            'url': 'https://picsum.photos/400/600?image=81',
            'preloadResource': True
        }
    ],
    width=360,
    height=640,
    preloadCount=2,  # Preload 2 stories ahead of current
)

Preloading Options:

Images are preloaded by default for smooth transitions, while videos are not to conserve bandwidth.


Component Properties

PropertyTypeDefaultDescription
idstringRequiredUnique identifier for the component used in Dash callbacks.
storieslistRequiredArray of image URLs (strings) or story objects with url, type, duration, header, etc.
rendererslist[]Array of custom renderer objects for advanced content types.
defaultIntervalnumber1200Default duration in milliseconds for which each story persists.
loaderComponentRipple loaderCustom loader component displayed while story loads from URL.
headerComponentDefault headerCustom header component displayed at top of each story. Receives header object from story data.
storyContainerStylesdict{}CSS styles object for the outer story container.
widthnumber or string360Width of the component. Accepts numbers (pixels) or strings (e.g., '100%', '100vw', 'inherit').
heightnumber or string640Height of the component. Accepts numbers (pixels) or strings (e.g., '100vh', '100%', 'inherit').
storyStylesdict{}CSS styles object to override default story content styles.
progressContainerStylesdict{}CSS styles object for the container wrapping all progress bars.
progressWrapperStylesdict{}CSS styles object for the container wrapping each individual progress bar.
progressStylesdict{}CSS styles object for the progress bars themselves.
loopboolFalseIf true, loops back to first story after the last story ends.
isPausedboolFalseControls story playback state. Set to true to pause, false to resume.
currentIndexnumberNoneCurrent story index (0-based). Set this to jump to a specific story programmatically.
onStoryStartfuncNoneCallback function triggered when a story starts playing.
onStoryEndfuncNoneCallback function triggered when a story finishes playing.
onAllStoriesEndfuncNoneCallback function triggered when all stories in the array have completed.
onNextfuncNoneCallback function triggered when user navigates to the next story (tap/press right or arrow key).
onPreviousfuncNoneCallback function triggered when user navigates to the previous story (tap/press left or arrow key).
keyboardNavigationboolFalseIf true, enables arrow key navigation. Also enables up arrow for "See More" and escape/down for closing.
preventDefaultboolFalseIf true, disables default click behavior on the component.
preloadCountnumber1Number of stories to preload ahead of the current story index for smoother transitions.
setPropsfunc(Dash Internal)Callback function to update component properties.
loading_stateobject(Dash Internal)Object describing the loading state of the component or its props.

Contributing

Contributions to dash-insta-stories are welcome! Please refer to the project's issues on GitHub for any feature requests or bug reports.

License

This project is licensed under the MIT License.

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: