Skip to content

๐Ÿ”˜ย ย Floating button

Submitted by Johannes Rieke

Summary

A button that stays fixed at the bottom right corner of the screen. Perfect for creating action buttons that are always accessible to users, such as chat interfaces.

Functions

floating_button

Display a floating action button that stays fixed at the bottom right corner.

This is similar to st.button but creates a button that floats above the content and remains visible even when scrolling. Only one floating button can be shown at a time.

Parameters:

Name Type Description Default
label str

A short label explaining to the user what this button is for. The label can optionally contain GitHub-flavored Markdown.

required
key str | None

An optional string to use as the unique key for the widget. If omitted, a key will be generated based on its content.

None
help str | None

A tooltip that gets displayed when the button is hovered over.

None
on_click Callable | None

An optional callback invoked when this button is clicked.

None
args tuple | None

An optional tuple of args to pass to the callback.

None
kwargs dict | None

An optional dict of kwargs to pass to the callback.

None
type str

The button type, either "primary" or "secondary". Defaults to "secondary". Note that "tertiary" is not supported.

'secondary'
icon str | None

An optional emoji or Material icon to display next to the button label. For Material icons, use the format ":material/icon_name:".

None
disabled bool

If True, disables the button. Defaults to False.

False

Returns:

Name Type Description
bool bool

True if the button was clicked on the last run of the app, False otherwise.

Raises:

Type Description
ValueError

If the type parameter is not "primary" or "secondary".

Examples:

>>> if st.floating_button(":material/chat:"):
...     st.write("Chat button clicked!")
>>> if st.floating_button("Add", icon=":material/add:"):
...     st.write("Add button clicked!")
Source code in src/streamlit_extras/floating_button/__init__.py
@extra
def floating_button(
    label: str,
    key: str | None = None,
    help: str | None = None,
    on_click: Callable[..., None] | None = None,
    args: tuple[Any, ...] | None = None,
    kwargs: dict[str, Any] | None = None,
    *,
    type: Literal["primary", "secondary"] = "secondary",
    icon: str | None = None,
    disabled: bool = False,
) -> bool:
    """Display a floating action button that stays fixed at the bottom right corner.

    This is similar to st.button but creates a button that floats above the content and
    remains visible even when scrolling. Only one floating button can be shown at a time.

    Args:
        label (str): A short label explaining to the user what this button is for.
            The label can optionally contain GitHub-flavored Markdown.
        key (str | None, optional): An optional string to use as the unique key for the
            widget. If omitted, a key will be generated based on its content.
        help (str | None, optional): A tooltip that gets displayed when the button is
            hovered over.
        on_click (Callable | None, optional): An optional callback invoked when this
            button is clicked.
        args (tuple | None, optional): An optional tuple of args to pass to the callback.
        kwargs (dict | None, optional): An optional dict of kwargs to pass to the callback.
        type (str, optional): The button type, either "primary" or "secondary".
            Defaults to "secondary". Note that "tertiary" is not supported.
        icon (str | None, optional): An optional emoji or Material icon to display next to
            the button label. For Material icons, use the format ":material/icon_name:".
        disabled (bool, optional): If True, disables the button. Defaults to False.

    Returns:
        bool: True if the button was clicked on the last run of the app, False otherwise.

    Raises:
        ValueError: If the type parameter is not "primary" or "secondary".

    Examples:
        >>> if st.floating_button(":material/chat:"):
        ...     st.write("Chat button clicked!")
        >>> if st.floating_button("Add", icon=":material/add:"):
        ...     st.write("Add button clicked!")
    """
    # Validate type parameter
    if type not in ["primary", "secondary"]:
        raise ValueError(
            f"Invalid type: {type}. Must be 'primary' or 'secondary', 'tertiary' is not "
            "supported for floating buttons. Using 'secondary'."
        )

    # Generate a unique key if none is provided
    if key is None:
        key = "fab"

    # Create CSS for the floating button
    st.html(
        f"""
        <style>
            .st-key-{key} button {{
                position: fixed;
                bottom: 3.5rem;
                right: 3.5rem;
                min-width: 3.5rem;
                min-height: 3.5rem;
                padding: 0.875rem;
                box-shadow: rgba(0, 0, 0, 0.16) 0px 4px 16px;
                z-index: 999;
                border-radius: 1rem;
                max-width: fit-content;
            }}
        </style>
        """,
    )

    # Create the button
    return st.button(
        label=label,
        key=key,
        on_click=on_click,
        args=args,
        kwargs=kwargs,
        type=type,
        disabled=disabled,
        help=help,
        icon=icon,
    )

Import:

from streamlit_extras.floating_button import floating_button # (1)!
  1. You should add this to the top of your .py file ๐Ÿ› 

Examples

example

def example() -> None:
    """Example usage of the floating_button function."""
    st.title("Floating action button demo")
    st.write("See in the bottom right corner :wink:")

    # Initialize chat messages in session state
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "assistant", "content": "Hello! How can I help you today?"}]

    # Chat dialog using decorator
    @st.dialog("Chat Support", width="large")
    def chat_dialog() -> None:
        # Create a container for chat messages with fixed height
        messages_container = st.container(height=400, border=False)

        # Display messages in the container
        with messages_container:
            # Display all messages from session state
            for message in st.session_state.messages:
                st.chat_message(message["role"]).write(message["content"])

        # Chat input (placed below the messages container in the UI)
        user_input = st.chat_input("Type a message...")

        # Handle new user input
        if user_input:
            messages_container.chat_message("user").write(user_input)
            st.session_state.messages.append({"role": "user", "content": user_input})

            # Add bot response to chat history
            messages_container.chat_message("assistant").write("Thanks for your message! This is a demo response.")
            st.session_state.messages.append(
                {
                    "role": "assistant",
                    "content": "Thanks for your message! This is a demo response.",
                }
            )

    # Handle FAB button click to open the dialog
    if floating_button(":material/chat: Chat"):
        chat_dialog()