Streamlit Custom Components + Vite + Vanilla JS

Create Component Based On vanilla JS
  • From the template folder
  • Create new component using vite and add init.py code for testing the component
  • $ mkdir vite_vanilla_component
    $ cd vite_vanilla_component
    $ npm init vite@latest frontend --template vanilla # npm v6 (v7 is different)
    $ touch __init__.py # command may be different in Windows
  • Add in the init.py code below
  • import os
    import streamlit.components.v1 as components
    
    _RELEASE = False
    
    if not _RELEASE:
      _component_func = components.declare_component(
        "vite_vanilla_component",
        url="http://localhost:3000", # vite dev server port
      )
    else:
      parent_dir = os.path.dirname(os.path.abspath(__file__))
      build_dir = os.path.join(parent_dir, "frontend/dist")
      _component_func = components.declare_component("vite_vanilla_component", path=build_dir)
    
    def my_component(name, key=None):
      component_value = _component_func(name=name, key=key, default=0)
      return component_value
    
    if not _RELEASE:
      import streamlit as st
      st.subheader("Component Test")
      num_clicks = my_component(name = "NameViteVanilla")
      st.markdown("You've clicked %s times!" % int(num_clicks))
  • install the frontend node libraries, streamlit-component-lib, create vite.config.js
  • $ cd frontend
    $ npm i
    $ npm i streamlit-component-lib
    $ touch vite.config.js
  • vite.config.js should look like below
  • export default {
      base: './'
    }
  • replace the content of main.js with the following (based on the reactless-template of the original component-template repo)
  • import { Streamlit } from "streamlit-component-lib"
    
    const span = document.body.appendChild(document.createElement("span"))
    const textNode = span.appendChild(document.createTextNode(""))
    const button = span.appendChild(document.createElement("button"))
    button.textContent = "Click Me!"
    
    let numClicks = 0
    let isFocused = false
    button.onclick = function() {
      numClicks += 1
      Streamlit.setComponentValue(numClicks)
    }
    
    button.onfocus = function() { isFocused = true }
    button.onblur = function() { isFocused = false }
    
    function onRender(event) {
      const data = event.detail
      if (data.theme) {
        const borderStyling = `1px solid var(${
          isFocused ? "--primary-color" : "gray"
        })`
        button.style.border = borderStyling
        button.style.outline = borderStyling
      }
      button.disabled = data.disabled
      let name = data.args["name"]
      textNode.textContent = `Hello, ${name}! ` + String.fromCharCode(160)
      Streamlit.setFrameHeight()
    }
    
    Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)
    Streamlit.setComponentReady()
    Streamlit.setFrameHeight()
    Running the example
    From the base directory, navigate to the frontend and serve it from a dev server:
    $ cd template/vite_vanilla_component/frontend
    $ npm run dev
    On a separate terminal, from base directory, navigate to and run the Streamlit app (assuming python environment has been activated):
    $ cd template
    $ streamlit run vite_vanilla_component/__init__.py
    You should see the image below, and clicking the button should increment the count.
    The component looks truncated. To fix this, set body style in the component to have margin: 0;.

    61

    This website collects cookies to deliver better user experience

    Streamlit Custom Components + Vite + Vanilla JS