Streamlit

repository·develop·Indexed 13 days ago

https://github.com/streamlit/streamlit

A framework that allows developers to transform Python scripts into interactive web applications for data science, dashboards, and chat apps. It includes a component library for custom components, a frontend development suite, and tools for server load testing using Playwright.

Tokens
82K
Snippets
224
Records
346
Agent score
97%

What's inside Streamlit

  1. Overview of Streamlit Frontend packages

    develop

    The Streamlit Frontend is a collection of packages that define the behaviors and layouts of a Streamlit App. Key packages include:

    • app: The core Streamlit app layout, bundled into the main Streamlit library.
    • component-lib: Library for building Streamlit custom components v1.
    • component-v2-lib: Support library for Streamlit Components v2.
    • connection: Handles establishing the Websocket connection.
    • eslint-plugin-streamlit-custom: ESLint plugin containing custom rules for the ecosystem.
    • lib: Supports a Streamlit "View" which contains elements, widgets, and layouts.
    • protobuf: Centralized protobuf code.
    • typescript-config: Shared TypeScript configuration used across all packages.
    • utils: Shared utility functions used across the frontend.
  2. Use @streamlit/component-v2-lib for Streamlit Component v2 development

    develop
    The @streamlit/component-v2-lib package provides the necessary support code and TypeScript types for developers authoring Streamlit Components (v2). Use this library to ensure your component is correctly typed and compatible with the Streamlit component lifecycle and communication protocols.
  3. API restrictions during parallel fragment execution

    develop

    When using parallel fragments (fragments running concurrently on worker threads), certain Streamlit APIs are prohibited to prevent non-deterministic UI behavior, cursor races, and disruptive side effects.

    Prohibited during parallel execution (worker threads):

    • @st.dialog: You cannot open a dialog while multiple fragments are running in parallel. However, you can open a dialog during a sequential fragment rerun (e.g., a button click within a fragment that triggers only that fragment to rerun).
    • st.switch_page: Navigating to a new page is prohibited during a parallel batch because it would cancel all other active parallel threads and cause race conditions on the destination page. This is allowed during sequential fragment reruns.
    • External container writes: You cannot write elements (widgets or non-widgets) to containers located outside the fragment's own delta path (e.g., st.sidebar or a parent st.container()). All writes must be contained within the fragment body.

    Allowed during parallel execution:

    • Most standard Streamlit APIs for element rendering are safe due to internal synchronization.
    • Execution control commands like st.rerun and st.stop are handled via cooperative cancellation.

    If you attempt a prohibited operation, Streamlit will raise a StreamlitAPIException.

  4. Behavior of fragments writing to SIDEBAR and BOTTOM

    develop

    Fragments writing directly to the st.sidebar or st.bottom roots are automatically wrapped. This is necessary because these roots often hold main-script content (like a header in the sidebar or a chat input at the bottom).

    By using a wrapper, the fragment's content is contained within a stable slot. This prevents the fragment from overwriting trailing neighbors in the sidebar or bottom area when the number of elements inside the fragment changes across reruns.

  5. Understand collected performance metrics

    develop

    The framework collects three categories of metrics:

    Server Metrics (sampled every 500ms via psutil)

    • memory_rss_mb: Resident Set Size in MB (start/end)
    • memory_rss_mb_peak: Peak RSS during test
    • memory_rss_mb_avg: Average RSS during test
    • memory_rss_mb_growth: RSS growth from start to end
    • cpu_percent_avg: Average CPU utilization
    • cpu_percent_peak: Peak CPU utilization
    • thread_count_max: Maximum number of server threads

    Session Metrics (per simulated user)

    • initial_load_time_ms: Time to first complete app render
    • rerun_times_ms: Times for script reruns after interactions
    • errors: Any errors encountered in the session

    Aggregate Metrics

    • sessions_completed: Successful sessions
    • sessions_failed: Failed or timed-out sessions
    • initial_load_time_ms: Percentiles (min, max, mean, p50, p95, p99) for initial load
    • rerun_time_ms: Percentiles (min, max, mean, p50, p95, p99) for reruns
  6. Handle indivisible long-running operations with st.background_task()

    develop

    Parallel fragments (parallel=True) are bounded to the script run; a barrier joins all threads before the run is complete. If a fragment contains an indivisible, very long operation (e.g., a 30-second model training call), the entire app will wait for that thread to finish before a new rerun can start.

    To prevent a slow operation from blocking the app and its ability to rerun, use st.background_task(). This moves the work outside the script run entirely, allowing the script to remain responsive while the task runs independently in a managed thread.

  7. Suppress automatic reruns with `on_change="ignore"`

    develop

    You can prevent stateful widgets from triggering a full script rerun every time their value changes by passing on_change="ignore". This is useful for batching multiple parameter adjustments (like sliders or selectboxes) before executing expensive computations or data processing.

    How it works

    1. Frontend Update: The widget's visual state updates immediately in the browser.
    2. Value Storage: The new value is stored in the frontend state but is not immediately sent to the Python backend.
    3. No Rerun: The Streamlit script does not rerun.
    4. Sync on Next Rerun: When a rerun is eventually triggered (e.g., by clicking an st.button), the frontend flushes the updated values to the backend. Your Python code will then receive the new values via the widget's return value or st.session_state.

    Warning: Values modified with on_change="ignore" are held in browser memory only. If the user refreshes the page before a rerun occurs, these changes are lost.

    # Adjust parameters without triggering reruns
    threshold = st.slider("Threshold", 0.0, 1.0, 0.5, on_change="ignore")
    learning_rate = st.slider("Learning Rate", 0.001, 0.1, 0.01, on_change="ignore")
    
    # Only run expensive computation when user clicks
    if st.button("Train Model"):
        # On this rerun, 'threshold' and 'learning_rate' will reflect the new values
        model = train_model(threshold, learning_rate)
  8. Handle ButtonColumn click events via session state

    develop

    When a button is clicked, the click information is stored in st.session_state[key] as a dictionary. This value is only present during the rerun triggered by the click; it resets to None on subsequent reruns.

    Click State Dictionary Structure:

    • row: The integer position (index) of the row in the original dataframe.
    • label: The full string label of the button that was clicked (including icon prefixes).

    Example access:

    if st.session_state.get("my_button_key"):
        click = st.session_state.my_button_key
        row_index = click["row"]
        button_label = click["label"]
  9. How fragments and targeted reruns work

    develop

    Streamlit uses a top-to-bottom rerun model where the entire script re-executes on every widget interaction. To avoid full-script reruns for specific UI regions, you can use @st.fragment.

    Scoped Reruns with @st.fragment

    A fragment is a function whose execution is scoped. When a widget inside a fragment changes, only that fragment re-executes, rather than the entire script. This allows for partial re-evaluation of specific UI components (like a single chart or a filter bar) without the overhead of a full app rerun.

    Targeted Reruns (Proposed/Upcoming)

    The framework is evolving to allow a widget's event handler to trigger a rerun of a specific, addressable fragment from outside that fragment. This is achieved by passing the fragment's unique key to st.rerun(). This enables cross-fragment communication (e.g., a filter widget in one part of the app triggering a rerun of a data visualization fragment in another) while maintaining the deterministic, re-computation-based model of Streamlit.

  10. Behavior of fragments writing to EVENT root (toasts and dialogs)

    develop

    Writes to the EVENT root (used by st.toast and dialogs) do not receive an implicit wrapper.

    This is by design because:

    • st.toast calls are one-shot effects where the frontend handles fresh payloads and auto-dismissal.
    • Dialogs are modal singletons that do not suffer from the positional interleaving/overwrite issues that affect containers like st.sidebar or st.bottom.
  11. How st.skeleton context manager works

    develop

    When using st.skeleton as a context manager (via a with statement), it follows a specific lifecycle designed for transient loading states:

    1. Immediate Display: The skeleton is shown immediately when the with block is entered.
    2. Delayed Re-show: If used in with mode, the initial skeleton is cleared and re-shown with a 0.5s delay to prevent flickering for extremely fast operations.
    3. Auto-Clear: Once the code inside the with block completes, the skeleton is automatically cleared from the UI.

    This pattern is ideal for wrapping data-loading functions where you want a visual indicator that disappears as soon as the data is ready.

    with st.skeleton(200):
        # Perform heavy data loading or computation
        data = load_expensive_data()
        st.dataframe(data)
    # Skeleton is automatically cleared here
  12. Choosing between a product spec and a tech spec

    develop

    Streamlit distinguishes between product and technical specifications based on the scope and target of the change.

    Product Spec (product-spec.md)

    Use a product spec when proposing new user-facing features or significant API changes. It focuses on the what and why:

    • User-facing problems being solved.
    • Proposed API designs.
    • Design mockups and UX decisions.
    • Expected behavior.

    Tech Spec (tech-spec.md)

    Use a tech spec for non-user-facing changes that are architecturally significant. It focuses on the how:

    • Internal architecture.
    • Protocol (proto) changes.
    • Frontend/backend design and split.
    • State management approaches.
    • Documentation of implementation alternatives and trade-offs.

    A single feature may require both a product-spec.md and a tech-spec.md in the same directory.