Custom component to display clickable images

Hi! I didnā€™t find a way to do this with existing components so I created a simple component to display images and receive the index of the last image that was clicked on.

screenshot

A more advanced example can be seen live here.

Installation

pip install st-clickable-images

Quickstart

import streamlit as st
from st_clickable_images import clickable_images

clicked = clickable_images(
    [
        "https://images.unsplash.com/photo-1565130838609-c3a86655db61?w=700",
        "https://images.unsplash.com/photo-1565372195458-9de0b320ef04?w=700",
        "https://images.unsplash.com/photo-1582550945154-66ea8fff25e1?w=700",
        "https://images.unsplash.com/photo-1591797442444-039f23ddcc14?w=700",
        "https://images.unsplash.com/photo-1518727818782-ed5341dbd476?w=700",
    ],
    titles=[f"Image #{str(i)}" for i in range(5)],
    div_style={"display": "flex", "justify-content": "center", "flex-wrap": "wrap"},
    img_style={"margin": "5px", "height": "200px"},
)

st.markdown(f"Image #{clicked} clicked" if clicked > -1 else "No image clicked")

Usage

clickable_images(paths,titles=[], div_style={}, img_style={}, key=None)

Displays one or several images and returns the index of the last image clicked on (or -1 before any click)

Parameters

  • paths (list): the list of URLS of the images
  • titles (list): the (optional) titles of the images
  • div_style (dict): a dictionary with the CSS property/value pairs for the div container
  • img_style (dict): a dictionary with the CSS property/value pairs for the images
  • key (str or None): an optional key that uniquely identifies this component. If this is None, and the componentā€™s arguments are changed, the component will be re-mounted in the Streamlit frontend and lose its current state
20 Likes

Awesome job @vivien! I know a lot of people have asked for this sort of functionality!

If you havenā€™t already, please add this to the Streamlit Components - Community Tracker so that others will be able to discover this easier :slight_smile:

Best,
Randy

3 Likes

Done! Thanks Randy

2 Likes

Hi @vivien I tried your component, but it doesnā€™t load the images for me. I just get the ā€œNo Image Clickedā€ text, but clickable_images() function isnā€™t doing anything for me. Perhaps a package versioning issue? Can you tell if there are any minimum package requirements?

1 Like

Upon further debugging, I found that paths argument is a list of urls and canā€™t be a list of local paths of the images currently. :+1:

1 Like

Hi @therochvoices. You can provide URLs but, if your images are local files, you can also open these files, encode the images in base64, and provide ā€œdata:image/jpeg;base64,[XXX]ā€ as paths (where [XXX] is replaced by the base64-encoded images)

2 Likes

Hello @vivien

This is a very cool component! Much needed!
Is there a way currently to show the title of the image above the image as well? (Currently the title shows while hovering the mouse over the image?). Thank you!

1 Like

Hi @MLbyML. Thanks. Iā€™m afraid this isnā€™t possible with the component as it is now (but it shouldnā€™t be too complicated to do it if you want to give it a try: the javascript code to display and style the images is pretty short).

2 Likes

@vivien thanks so much for sharing this, a fabulous widget which came in very handy for me today - using it to select an image when then calls an Azure image analysis service to return a description plus lists of categories, tags, landmarks, people, and brands. Love it!

2 Likes

@LostGoatOnHill Thanks for the feedback. It sounds like a cool app!

1 Like

@vivien you helped make this possible :slight_smile:

1 Like

FYI, I created another component which is a generalization of this one: st-click-detector. It works not only on images but also on all HTML links.

3 Likes

Hi vivien, good day can you pls provide an exact example of this, where the image file is locally found. I am having trouble doing it. Thanks.

1 Like

Hi @data.science. Here is an example:

import base64
import streamlit as st
from st_clickable_images import clickable_images

images = []
for file in ["image1.jpeg", "image2.jpeg"]:
    with open(file, "rb") as image:
        encoded = base64.b64encode(image.read()).decode()
        images.append(f"data:image/jpeg;base64,{encoded}")

clicked = clickable_images(
    images,
    titles=[f"Image #{str(i)}" for i in range(2)],
    div_style={"display": "flex", "justify-content": "center", "flex-wrap": "wrap"},
    img_style={"margin": "5px", "height": "200px"},
)

st.markdown(f"Image #{clicked} clicked" if clicked > -1 else "No image clicked")
4 Likes

Hi @vivien thanks for the component, I like it. I am struggling to change the state of it though. I need a functionality which allows to state goes back to default (-1). Say I have an image which I use for saving report. I have a few more radio and dropdown lists to adjust the report page. Every time I click a radio button or dropdown list, it regenerates a report and saves it. I only want to save the report, if I click that image again. Any help appreciated, thanks.

1 Like

Hi @Omer_Eker. Iā€™d first invite you to switch to st-click-detector which is more flexible and general than st-clickable-images.

More specifically regarding the situation you describe, it might be possible to use st.session_state to keep track of the last component with which the user interacted to decide whether to trigger the saving of the report.

1 Like

@vivien thanks for the reply. I tried a few workaround including using session_states but didnā€™t help. I am leaving a minimum reproducible example code here. Can you help me how would you do it?

import streamlit as st
from st_clickable_images import clickable_images

with st.sidebar:
    st.radio("Radio",[1,2,3])

clicked = clickable_images(
    ["https://icons.iconarchive.com/icons/custom-icon-design/pretty-office-7/256/Save-icon.png"],
    div_style={"display": "flex", "justify-content": "left", "flex-wrap": "wrap"},
    img_style={"margin": "10px", "height": "250px"},
    key="clicked_images",
)

if clicked == 0:
    st.subheader("Saving Report..")
else:
    st.subheader(f"Selected Image No: #{clicked}")
1 Like

Hey @Omer_Eker. Check this out (with st-click-detector instead of st-clickable-images):

import streamlit as st
from st_click_detector import click_detector

if "n_clicks" not in st.session_state:
    st.session_state["n_clicks"] = "0"

with st.sidebar:
    choice = st.radio("Radio", [1, 2, 3])

id = str(int(st.session_state["n_clicks"]) + 1)

content = f"<a href='#' id='{id}'><img src='https://icons.iconarchive.com/icons/custom-icon-design/pretty-office-7/256/Save-icon.png'></a>"

clicked = click_detector(content, key="click_detector")

if clicked != "" and clicked != st.session_state["n_clicks"]:
    st.session_state["n_clicks"] = clicked
    st.subheader("Saving Report..")
else:
    st.subheader(f"Choice: #{choice}")
3 Likes

Hi @vivien i am struggling witht the part where when i click an image i want all the remaining images to be cleared and just that 1 image be displayed. Any help on how can i achieve it.

1 Like

Hi @vivien, this is a great component. Is it possible to highlight the selected image?

1 Like