Button using Custom Component

The st.Button(…) return true if it was clicked on the last run and otherwise False. Is there a way to reproduce the same functionality in a custom component ? For example, is there a way to replace st.Button(…) with something like my_button(…) ?

Hey @Sarthak_Jain,

Sure, this is actually what the Streamlit Component template consists of :slight_smile: a my_component() which builds a button next to a label tag! Definitely test it if you haven’t yet :wink:

Cheers,
Fanilo

1 Like

Hi

So I did look at that component but the functionality I am looking for is slightly different.

In the template, at an given run, we can know how many times the button was clicked in the past (and this information persist if the button was not pressed on the last run). For example, here is a sequence of events -

  1. Click button -> Do something with value of button (= 1)
  2. Click button -> Do something with value of button (= 2)
  3. Interact with other stuff -> Button was not clicked, don’t do anything! But it still has value 2 .
  4. Click button -> Do something with value of button (= 3)

But step 3 is not possible .

Take following snippet as example,

value = st.selectbox(...)
if my_component_button() :
       .... do something ....

Now, this might be possible if I used SessionState but I was looking for a way that is built into Streamlit (I assume it might be since the st.button do provide this functionality) .

Hello @Sarthak_Jain,

Without relying on session states, this is the best way I have found. One issue though, when the button states goes from True to False (if you interact with the app after having clicked the button), the script reruns twice.

import {
  Streamlit,
  withStreamlitConnection
} from "streamlit-component-lib";
import React, { useEffect } from "react"

let state = "idle"

const MyButton = ({ args, disabled }: any) => {
  useEffect(() => {
    Streamlit.setFrameHeight()

    // Button was just clicked, and component code reruns
    // But we don't want to set component value to false yet!
    if (state === "clicked") {
      state = "reset"
    }

    // A rerun was done, executing the component code again
    // So we can safely set the component value to False (which triggers another rerun)
    else if (state === "reset") {
      Streamlit.setComponentValue(false)
      state = "idle"
    }
  })

  const handleClick = () => {
    Streamlit.setComponentValue(true)
    state = "clicked"
  }

  return (
    <button onClick={handleClick} disabled={disabled}> 
      {args.label}
    </button>
  )
}

export default withStreamlitConnection(MyButton)

And the python part, just returning the component value.

import os
import streamlit.components.v1 as components

_RELEASE = False

if not _RELEASE:
    _my_button = components.declare_component("my_button", url="http://localhost:3001",)
else:
    parent_dir = os.path.dirname(os.path.abspath(__file__))
    build_dir = os.path.join(parent_dir, "frontend/build")
    _my_button = components.declare_component("my_button", path=build_dir)

def my_button(label, key=None):
    return _my_button(label=label, key=key, default=False)
4 Likes

Small update, I’ve opened a new GitHub issue to get an official solution for this feature.