User Input Widgets in Streamlit Slider, Text Input, Selectbox
Widgets are the building blocks of every interactive Streamlit app. This guide covers every input widget you will need — slider, text input, selectbox, checkbox, radio, multiselect, date picker, file uploader and forms — with clear examples and the key rule behind how Streamlit reruns work.
Shashank ShekharJanuary 2025 · Streamlit
An app that only displays information is a dashboard. An app that lets users interact with it — adjusting parameters, filtering data, uploading files — is a tool. Widgets are what make the difference.
Streamlit makes adding widgets incredibly simple. A single line of Python adds a slider, a dropdown or a text box, and Streamlit handles all the HTML, CSS and JavaScript for you. The widget returns its current value as a Python variable, and you use that value to control everything else in your app.
This guide covers every widget you will need, how they work together, and the important concept of reruns that makes Streamlit tick.
Download the Complete CodeA ready-to-run Streamlit app demonstrating every widget in this guide — slider, selectbox, forms, sidebar and more.
Python script · Full app · Free
How Reruns Work — The Key to Understanding Streamlit
Before widgets make sense, you need to understand one fundamental thing about how Streamlit runs your code.
Every time a user interacts with any widget, Streamlit reruns your entire script from top to bottom. This is different from how most web frameworks work. There are no event handlers or callback functions. Your script just runs again, and the widgets return their new values.
This means writing interactive apps is simple: just use the widget's return value in your code, and every time the user changes the widget, the code that depends on it automatically uses the new value.
Python — the rerun model in one simple example
importstreamlitasst# Every time the slider moves, this entire script reruns# st.slider() returns the current value of the sliderage=st.slider('How old are you?',min_value=0,max_value=100,value=25)# This line uses the slider value — it updates automatically every rerunst.write(f'You selected: {age} years old')ifage<18:st.info('You are a minor.')elifage<65:st.success('You are a working-age adult.')else:st.warning('You are a senior citizen.')
ℹ️Run any Streamlit app with:streamlit run your_app.py from the terminal. It opens automatically in your browser at localhost:8501.
Widget Overview
Here is every widget covered in this guide at a glance:
st.text_input()
Single line of text
returns: str
st.text_area()
Multi-line text box
returns: str
st.number_input()
Numeric value with arrows
returns: int or float
st.slider()
Drag handle on a track
returns: int, float or tuple
st.selectbox()
Pick one from a dropdown
returns: any
st.multiselect()
Pick multiple from a list
returns: list
st.radio()
Pick one from radio buttons
returns: any
st.checkbox()
Tick box on or off
returns: bool
st.button()
Click to trigger action
returns: bool
st.date_input()
Calendar date picker
returns: date
st.time_input()
Time picker
returns: time
st.file_uploader()
Upload files from disk
returns: UploadedFile
st.color_picker()
Visual colour selector
returns: str (hex)
Text Input
st.text_input() shows a single-line text box. The first argument is the label shown above it. It returns whatever the user has typed as a string. If nothing is typed yet, it returns an empty string or the default value you set.
Python — st.text_input() with all common options
importstreamlitasst# Basic text inputname=st.text_input('Enter your name')# With a default valuecity=st.text_input('City',value='Bengaluru')# With placeholder text (shown when box is empty)email=st.text_input('Email address',placeholder='you@example.com')# Password input — hides the text as the user typespassword=st.text_input('Password',type='password')# Use the returned valuesifname:st.write(f'Hello, {name}! You are from {city}.')else:st.write('Please enter your name above.')
Text Area — Multi-line Input
Use st.text_area() when you need the user to type a longer piece of text — a description, a message, a block of code to analyse. It works identically to text_input but renders as a multi-line box.
Python — st.text_area() for longer text input
# height controls the initial size in pixelsdescription=st.text_area('Describe your project',placeholder='Tell us what you are building...',height=150)ifdescription:word_count=len(description.split())st.caption(f'{word_count} words')
Number Input
st.number_input() shows a text box with up/down arrows for numeric input. It is better than a text box for numbers because it validates that the user cannot type letters and it adds convenient increment/decrement buttons.
Python — st.number_input() for integers and floats
The slider is one of the most satisfying widgets to use. It lets users drag a handle along a track to pick a number. It is great for parameters like age, year, temperature or any value within a known range where typing feels unnecessary.
Python — st.slider() for numbers, floats and dates
# Integer slideryear=st.slider('Select a year',min_value=2000,max_value=2024,value=2020)# Float slider with stepconfidence=st.slider('Confidence threshold',min_value=0.0,max_value=1.0,value=0.5,step=0.05)# Date sliderfromdatetimeimportdateselected_date=st.slider('Select a date',min_value=date(2020,1,1),max_value=date.today(),value=date(2022,6,1))st.write(f'Year: {year} | Confidence: {confidence} | Date: {selected_date}')
Range Slider — Select a Min and Max
Pass a tuple as the value parameter and you get a range slider with two handles. The widget returns a tuple of two values — the lower and upper bounds of the selected range. This is perfect for filtering data.
Python — range slider for filtering a dataset
importpandasaspd# value=(min, max) creates a range slider with two handlesprice_range=st.slider('Price range (USD)',min_value=0,max_value=10000,value=(500,5000),# tuple = range sliderstep=100)min_price,max_price=price_rangest.write(f'Showing products from ${min_price:,} to ${max_price:,}')# Use the range to filter a dataframe# df_filtered = df[(df['price'] >= min_price) & (df['price'] <= max_price)]
Selectbox — Pick One From a Dropdown
st.selectbox() shows a dropdown where the user picks exactly one option. You pass a list of options and it returns whichever one the user selected. The options can be strings, numbers or any Python objects.
Python — st.selectbox() with strings and objects
# Basic selectboxcountry=st.selectbox('Select your country',options=['India','USA','UK','Germany','Japan'])# With a default selection (by index)model=st.selectbox('Choose ML model',options=['Linear Regression','Random Forest','XGBoost','Neural Network'],index=1# 'Random Forest' is selected by default)# Selectbox over a pandas DataFrame columnimportpandasaspddf=pd.read_csv('data.csv')selected=st.selectbox('Choose a column',df.columns)st.write(f'You chose: {country}')st.write(f'Model selected: {model}')
Multiselect — Pick Several Options
st.multiselect() lets the user pick as many options as they want. It returns a list of the selected items. If nothing is selected, it returns an empty list.
Python — st.multiselect() for filtering with multiple values
languages=st.multiselect('Which languages do you know?',options=['Python','JavaScript','Java','Go','Rust','TypeScript'],default=['Python']# pre-selected options)iflanguages:st.write(f'You know {len(languages)} language(s): {", ".join(languages)}')else:st.warning('Please select at least one language.')# Use multiselect to filter a DataFrame# df_filtered = df[df['language'].isin(languages)]
Radio Buttons — Pick One Visually
st.radio() shows all options at once as radio buttons rather than hiding them in a dropdown. Use it when you have a small number of choices (3-5) and you want all options to be immediately visible.
Python — st.radio() for a small set of visible choices
# Vertical radio (default)chart_type=st.radio('Chart type',options=['Bar chart','Line chart','Scatter plot'])# Horizontal radio — better when options are shorttheme=st.radio('Theme',options=['Light','Dark','System'],horizontal=True)st.write(f'Showing {chart_type} in {theme} mode')
Checkbox — On or Off
st.checkbox() returns True when ticked and False when not. It is perfect for toggling sections of your app on and off, showing or hiding data, and enabling optional features.
Python — st.checkbox() to toggle sections and options
importpandasaspd# Show or hide a section based on checkboxshow_raw_data=st.checkbox('Show raw data')ifshow_raw_data:df=pd.DataFrame({'A':[1,2,3],'B':[4,5,6]})st.dataframe(df)# With a default state (checked by default)include_tax=st.checkbox('Include tax in total',value=True)price=1000total=price*1.18ifinclude_taxelsepricest.metric('Total',f'${total:,.2f}')
Button — Trigger an Action
st.button() returns True only on the rerun triggered by the click itself. On the next rerun it goes back to False. This means button clicks are momentary — use them to trigger one-time actions like submitting data or running a calculation, not for toggles (use checkbox for that).
⚠️Button state resets every rerun. Do not use st.button() to toggle something visible in the UI — because on the next user interaction the button will be False again and the thing will disappear. Use st.checkbox() or st.session_state for persistent toggles.
Date and Time Pickers
Python — date input, time input and date range
fromdatetimeimportdate,time# Single date pickerbirthday=st.date_input('Date of birth',value=date(1995,6,15),min_value=date(1900,1,1),max_value=date.today())# Date range — returns a tuple of (start_date, end_date)date_range=st.date_input('Select a date range',value=(date(2024,1,1),date(2024,12,31)))iflen(date_range)==2:start,end=date_rangest.write(f'{(end - start).days} days selected')# Time pickermeeting_time=st.time_input('Meeting time',value=time(9,30),step=900# step in seconds — 900 = 15 minute intervals)
File Uploader
st.file_uploader() lets users upload files directly from their computer into your app. It returns an UploadedFile object (or None if no file has been uploaded yet). The object behaves like a file — you can read it, pass it to pandas, PIL or any other library.
Python — uploading CSV and image files
importpandasaspdfromPILimportImage# Upload a single CSV fileuploaded_csv=st.file_uploader('Upload a CSV file',type=['csv'])ifuploaded_csvisnotNone:df=pd.read_csv(uploaded_csv)st.write(f'Loaded {df.shape[0]} rows and {df.shape[1]} columns')st.dataframe(df.head())# Upload an imageuploaded_img=st.file_uploader('Upload an image',type=['jpg','jpeg','png'])ifuploaded_imgisnotNone:img=Image.open(uploaded_img)st.image(img,caption='Uploaded image',use_column_width=True)# Accept multiple files at oncefiles=st.file_uploader('Upload multiple files',accept_multiple_files=True)st.write(f'{len(files)} file(s) uploaded')
Colour Picker
st.color_picker() opens a colour selector and returns the chosen colour as a hex string like #FF5733. You can use this directly in any matplotlib or plotly chart.
Python — colour picker for chart customisation
colour=st.color_picker('Pick a chart colour',value='#ffb800')st.write(f'Selected: {colour}')# Use the hex colour in a matplotlib chartimportmatplotlib.pyplotaspltimportnumpyasnpfig,ax=plt.subplots()ax.bar(['A','B','C'],[3,7,5],color=colour)st.pyplot(fig)
Sidebar Widgets — Keeping the Main Area Clean
When your app has many widgets, putting them all in the main area gets cluttered. Move your controls to the sidebar and keep the main area for the output — charts, tables, results. Any widget works in the sidebar: just replace st. with st.sidebar.
Python — moving all controls to the sidebar
# Method 1: use st.sidebar. prefix directlymodel=st.sidebar.selectbox('Model',['Linear','Random Forest'])epochs=st.sidebar.slider('Epochs',10,200,50)# Method 2: with block — cleaner for many widgetswithst.sidebar:st.header('Controls')dataset=st.selectbox('Dataset',['Iris','Titanic','Boston'])test_size=st.slider('Test size',0.1,0.5,0.2)normalise=st.checkbox('Normalise features',value=True)run_btn=st.button('Train model',type='primary')# Main area shows outputst.title('Model Results')ifrun_btn:st.success(f'Training {dataset} with {model} — test size {test_size}')
Forms — Batch Multiple Inputs
Normally Streamlit reruns every time any widget changes. If you have ten widgets and a slow computation, this means the app reruns ten times as the user fills in the form — which is terrible for performance.
Forms solve this. Widgets inside a form do not trigger a rerun as the user changes them. The rerun only happens when the user clicks the submit button. All widget values are collected and processed together in one single rerun.
Python — wrapping widgets in a form to batch submissions
withst.form('user_profile'):st.subheader('Create your profile')name=st.text_input('Full name')age=st.slider('Age',18,90,25)role=st.selectbox('Role',['Developer','Designer','Manager'])skills=st.multiselect('Skills',['Python','SQL','ML','React'])bio=st.text_area('Short bio',height=80)# submit_button triggers the rerunsubmitted=st.form_submit_button('Save profile',type='primary')# This block runs once when the form is submittedifsubmitted:ifnotname:st.error('Name is required.')else:st.success(f'Profile saved for {name}!')st.json({'name':name,'age':age,'role':role,'skills':skills})
Widgets in Columns — Side by Side
By default every widget takes the full width. Use st.columns() to place widgets side by side and make better use of horizontal space.
Python — placing widgets side by side with columns
# Equal width columnscol1,col2,col3=st.columns(3)withcol1:first_name=st.text_input('First name')withcol2:last_name=st.text_input('Last name')withcol3:age=st.number_input('Age',1,120,25)# Unequal column widths — [2,1] means left is twice as wide as rightc_main,c_side=st.columns([2,1])withc_main:query=st.text_input('Search',placeholder='Type to search...')withc_side:sort=st.selectbox('Sort by',['Name','Date','Score'])
Full Example App — Data Explorer
Here is a complete working Streamlit app that uses most of the widgets from this guide together to build a simple data explorer:
Python — complete data explorer app using multiple widgets
importstreamlitasstimportpandasaspdimportnumpyasnpst.title('Sales Data Explorer')# ── Sidebar controls ──────────────────────────────────withst.sidebar:st.header('Filters')region=st.multiselect('Region',['North','South','East','West'],default=['North','South'])revenue_range=st.slider('Revenue range (USD)',0,100000,(10000,80000),5000)chart_type=st.radio('Chart type',['Bar','Line'],horizontal=True)show_table=st.checkbox('Show data table',value=True)# ── Generate dummy data ───────────────────────────────np.random.seed(42)df=pd.DataFrame({'Month':['Jan','Feb','Mar','Apr','May','Jun'],'Revenue':np.random.randint(20000,90000,6),'Region':np.random.choice(['North','South','East','West'],6)})# ── Apply filters ─────────────────────────────────────filtered=df[df['Region'].isin(region)&(df['Revenue']>=revenue_range[0])&(df['Revenue']<=revenue_range[1])]# ── Show metrics ──────────────────────────────────────m1,m2,m3=st.columns(3)m1.metric('Rows shown',len(filtered))m2.metric('Total revenue',f"${filtered['Revenue'].sum():,}")m3.metric('Avg revenue',f"${filtered['Revenue'].mean():,.0f}")# ── Chart ─────────────────────────────────────────────ifnotfiltered.empty:ifchart_type=='Bar':st.bar_chart(filtered.set_index('Month')['Revenue'])else:st.line_chart(filtered.set_index('Month')['Revenue'])else:st.warning('No data matches your filters.')# ── Conditional table ─────────────────────────────────ifshow_table:st.dataframe(filtered,use_container_width=True)
Quick Reference
Widget
Returns
Use When
st.text_input()
str
Short free text — names, emails, search queries
st.text_area()
str
Multi-line text — descriptions, comments, code
st.number_input()
int or float
Exact numeric value with precise control
st.slider()
number or tuple
Value within a known range — age, year, threshold
st.selectbox()
any
Pick exactly one from a list (more than 5 options)
st.multiselect()
list
Pick any number from a list — filters, tags
st.radio()
any
Pick exactly one — 2 to 5 options, all visible
st.checkbox()
bool
Toggle something on/off persistently
st.button()
bool
One-time actions — run, submit, reset
st.date_input()
date or tuple
Date selection or date range
st.file_uploader()
UploadedFile
Load CSV, images or any file from user's disk
st.form()
—
Group widgets — submit all values together
⚡ Key Takeaways
Every widget interaction triggers a full rerun of your script from top to bottom. This is Streamlit's core model — there are no event handlers. Just use the widget's return value and everything updates automatically.
Every widget returns a Python value you store in a variable. st.slider() returns a number, st.text_input() returns a string, st.multiselect() returns a list.
Use st.text_input() for short free text (name, email, search). Use st.text_area() when you need paragraph-length input.
Use st.slider() with a tuple value to create a range slider — perfect for filtering data by a min/max range. The return value is a tuple you can unpack.
Use st.selectbox() when the user picks exactly one option from a longer list. Use st.radio() when the list is short (2-5) and you want all options visible at once.
st.checkbox() returns True/False and keeps its state across reruns — good for toggling sections. st.button() is True only on the click rerun — good for one-time actions like running a model.
Move your controls to the sidebar with with st.sidebar: to keep the main area clean for charts and results. Use st.columns() to place widgets side by side.
Wrap slow or complex inputs in a st.form() block. Widgets inside a form do not trigger reruns until the user clicks submit — preventing unnecessary computation as the user fills in the form.
st.file_uploader() returns an UploadedFile object that behaves like a regular file. Pass it directly to pd.read_csv(), PIL.Image.open() or any library that accepts file objects.
Run your app with streamlit run app.py in the terminal. It opens in your browser at localhost:8501 and hot-reloads whenever you save the file.
Download the Complete CodeA ready-to-run Streamlit app with all widgets from this guide — slider, selectbox, forms, sidebar, file uploader and more.