└── content └── develop ├── _index.md ├── api-reference ├── _index.md ├── caching-and-state │ ├── _index.md │ ├── cache-data.md │ ├── cache-resource.md │ ├── context.md │ ├── experimental_get_query_params.md │ ├── experimental_set_query_params.md │ ├── query_params.md │ └── session_state.md ├── charts │ ├── _index.md │ ├── altair_chart.md │ ├── area_chart.md │ ├── bar_chart.md │ ├── bokeh_chart.md │ ├── graphviz_chart.md │ ├── line_chart.md │ ├── map.md │ ├── plotly_chart.md │ ├── pydeck_chart.md │ ├── pyplot.md │ ├── scatter_chart.md │ └── vega_lite_chart.md ├── chat │ ├── _index.md │ ├── chat-input.md │ └── chat-message.md ├── command-line │ ├── _index.md │ ├── cache.md │ ├── config.md │ ├── docs.md │ ├── hello.md │ ├── help.md │ ├── init.md │ ├── run.md │ └── version.md ├── configuration │ ├── _index.md │ ├── config-toml.md │ ├── get_option.md │ ├── set_option.md │ └── set_page_config.md ├── connections │ ├── _index.md │ ├── connection.md │ ├── connections-baseconnection.md │ ├── connections-experimentalbaseconnection.md │ ├── connections-snowflake.md │ ├── connections-snowpark.md │ ├── connections-sql.md │ ├── experimental-connection.md │ ├── secrets-toml.md │ └── secrets.md ├── control-flow │ ├── _index.md │ ├── dialog.md │ ├── experimental_rerun.md │ ├── form.md │ ├── form_submit_button.md │ ├── fragment.md │ ├── rerun.md │ └── stop.md ├── custom-components │ ├── _index.md │ ├── declare_component.md │ ├── html.md │ └── iframe.md ├── data │ ├── _index.md │ ├── column_config │ │ ├── _index.md │ │ ├── areachartcolumn.md │ │ ├── barchartcolumn.md │ │ ├── checkboxcolumn.md │ │ ├── column.md │ │ ├── datecolumn.md │ │ ├── datetimecolumn.md │ │ ├── imagecolumn.md │ │ ├── jsoncolumn.md │ │ ├── linechartcolumn.md │ │ ├── linkcolumn.md │ │ ├── listcolumn.md │ │ ├── numbercolumn.md │ │ ├── progresscolumn.md │ │ ├── selectboxcolumn.md │ │ ├── textcolumn.md │ │ └── timecolumn.md │ ├── data_editor.md │ ├── dataframe.md │ ├── experimental_data_editor.md │ ├── json.md │ ├── metric.md │ └── table.md ├── layout │ ├── _index.md │ ├── columns.md │ ├── container.md │ ├── empty.md │ ├── expander.md │ ├── popover.md │ ├── sidebar.md │ └── tabs.md ├── media │ ├── _index.md │ ├── audio.md │ ├── image.md │ ├── logo.md │ └── video.md ├── navigation │ ├── _index.md │ ├── navigation.md │ ├── page.md │ └── switch_page.md ├── status │ ├── _index.md │ ├── balloons.md │ ├── error.md │ ├── exception.md │ ├── info.md │ ├── progress.md │ ├── snow.md │ ├── spinner.md │ ├── status.md │ ├── success.md │ ├── toast.md │ └── warning.md ├── testing │ ├── _index.md │ ├── st.testing.v1.AppTest.md │ └── testing_elements.md ├── text │ ├── _index.md │ ├── badge.md │ ├── caption.md │ ├── code.md │ ├── divider.md │ ├── echo.md │ ├── header.md │ ├── help.md │ ├── html.md │ ├── latex.md │ ├── markdown.md │ ├── subheader.md │ ├── text.md │ └── title.md ├── user │ ├── _index.md │ ├── login.md │ ├── logout.md │ └── user.md ├── widgets │ ├── _index.md │ ├── audio_input.md │ ├── button.md │ ├── camera_input.md │ ├── checkbox.md │ ├── color_picker.md │ ├── date_input.md │ ├── download_button.md │ ├── feedback.md │ ├── file_uploader.md │ ├── link_button.md │ ├── multiselect.md │ ├── number_input.md │ ├── page_link.md │ ├── pills.md │ ├── radio.md │ ├── segmented_control.md │ ├── select_slider.md │ ├── selectbox.md │ ├── slider.md │ ├── text_area.md │ ├── text_input.md │ ├── time_input.md │ └── toggle.md └── write-magic │ ├── _index.md │ ├── magic.md │ ├── write.md │ └── write_stream.md ├── concepts ├── _index.md ├── app-design │ ├── _index.md │ ├── animate-elements.md │ ├── button-behavior-and-examples.md │ ├── custom-classes.md │ ├── dataframes.md │ ├── multithreading.md │ └── timezone-handling.md ├── app-testing │ ├── _index.md │ ├── automate-tests.md │ ├── beyond-the-basics.md │ ├── cheat-sheet.md │ ├── examples.md │ └── get-started.md ├── architecture │ ├── _index.md │ ├── app-chrome.md │ ├── architecture.md │ ├── caching.md │ ├── forms.md │ ├── fragments.md │ ├── run-your-app.md │ ├── session-state.md │ └── widget-behavior.md ├── configuration │ ├── _index.md │ ├── https.md │ ├── options.md │ ├── static-file-serving.md │ └── theming.md ├── connections │ ├── _index.md │ ├── authentication.md │ ├── connecting-to-data.md │ ├── secrets-management.md │ └── security-reminders.md ├── custom-components │ ├── _index.md │ ├── components-api.md │ ├── create-component.md │ ├── limitations.md │ └── publish-component.md └── multipage-apps │ ├── _index.md │ ├── overview.md │ ├── page-and-navigation.md │ ├── page_directory.md │ └── widgets.md ├── quick-references ├── _index.md ├── api-cheat-sheet.md ├── prerelease-features.md └── release-notes │ ├── 2019.md │ ├── 2020.md │ ├── 2021.md │ ├── 2022.md │ ├── 2023.md │ ├── 2024.md │ ├── 2025.md │ └── _index.md └── tutorials ├── _index.md ├── authentication ├── _index.md ├── google.md └── microsoft.md ├── databases ├── _index.md ├── aws-s3.md ├── bigquery.md ├── gcs.md ├── mongodb.md ├── mssql.md ├── mysql.md ├── neon.md ├── postgresql.md ├── private-gsheet.md ├── public-gsheet.md ├── snowflake.md ├── supabase.md ├── tableau.md ├── tidb.md └── tigergraph.md ├── elements ├── _index.md ├── charts │ └── annotate-altair-chart.md └── dataframes │ ├── row-selections (old).md │ └── row_selections.md ├── execution-flow ├── _index.md └── fragments │ ├── create-a-multiple-container-fragment.md │ ├── start-and-stop-fragment-auto-reruns.md │ └── trigger-a-full-script-rerun-from-a-fragment.md ├── llms ├── _index.md ├── chat-response-feedback.md ├── chat-response-revision.md ├── conversational-apps.md └── llm-quickstart.md └── multipage-apps ├── _index.md ├── custom-navigation.md └── dynamic-navigation.md
1 | --- 2 | title: Develop 3 | slug: /develop 4 | --- 5 | 6 | # Develop 7 | 8 | Get all the information you need to build beautiful, performant web apps with Streamlit! 9 | 10 | 11 | <InlineCallout 12 | color="indigo-70" 13 | icon="book" 14 | bold="Concepts." 15 | href="/develop/concepts" 16 | >Learn how Streamlit works with in-depth guides to our execution model and features. 17 | <InlineCallout 18 | color="indigo-70" 19 | icon="list" 20 | bold="API reference." 21 | href="/develop/api-reference" 22 | >Learn about our API with function definitions and examples. 23 | <InlineCallout 24 | color="indigo-70" 25 | icon="auto_awesome" 26 | bold="Tutorials." 27 | href="/develop/tutorials" 28 | >Follow step-by-step instructions to build example apps and useful snippets. 29 | <InlineCallout 30 | color="indigo-70" 31 | icon="bolt" 32 | bold="Quick references." 33 | href="/develop/quick-reference" 34 | >Check out our quick references for easy access to convenient information like our changelog, cheat sheet, pre-release features, and roadmap. 35 | 36 |
1 | ---
2 | title: API Reference
3 | slug: /develop/api-reference
4 | ---
5 |
6 | # API reference
7 |
8 | Streamlit makes it easy for you to visualize, mutate, and share data. The API
9 | reference is organized by activity type, like displaying data or optimizing
10 | performance. Each section includes methods associated with the activity type,
11 | including examples.
12 |
13 | Browse our API below and click to learn more about any of our available commands! 🎈
14 |
15 | ## Display almost anything
16 |
17 | ### Write and magic
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
python 30 | st.write("Hello **world**!") 31 | st.write(my_data_frame) 32 | st.write(my_mpl_figure) 33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Write generators or streams to the app with a typewriter effect.
41 |
42 | python 43 | st.write_stream(my_generator) 44 | st.write_stream(my_llm_stream) 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Any time Streamlit sees either a variable or literal value on its own line, it automatically writes that to your app using st.write
53 |
54 | python 55 | "Hello **world**!" 56 | my_data_frame 57 | my_mpl_figure 58 |
59 |
60 |
61 |
62 |
63 | ### Text elements
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Display string formatted as Markdown. 75 | 76 |
python 77 | st.markdown("Hello **world**!") 78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | Display text in title formatting.
88 |
89 | python 90 | st.title("The app title") 91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | Display text in header formatting.
101 |
102 | python 103 | st.header("This is a header") 104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | Display text in subheader formatting.
114 |
115 | python 116 | st.subheader("This is a subheader") 117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | Display a small, colored badge.
127 |
128 | python 129 | st.badge("New") 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | Display text in small font.
140 |
141 | python 142 | st.caption("This is written small caption text") 143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | Display a code block with optional syntax highlighting.
153 |
154 | python 155 | st.code("a = 1234") 156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | Display some code in the app, then execute it. Useful for tutorials.
166 |
167 | python 168 | with st.echo(): 169 | st.write('This code will be printed') 170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | Display mathematical expressions formatted as LaTeX.
180 |
181 | python 182 | st.latex("\int a x^2 \,dx") 183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | Write fixed-width and preformatted text.
193 |
194 | python 195 | st.text("Hello world") 196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | Display a horizontal rule.
206 |
207 | python 208 | st.divider() 209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | Display object’s doc string, nicely formatted.
217 |
218 | python 219 | st.help(st.write) 220 | st.help(pd.DataFrame) 221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 | Renders HTML strings to your app.
229 |
230 | python 231 | st.html("<p>Foo bar.</p>") 232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 | Display annotated text in Streamlit apps. Created by @tvst.
245 |
246 | python 247 | annotated_text("This ", ("is", "verb"), " some ", ("annotated", "adj"), ("text", "noun"), " for those of ", ("you", "pronoun"), " who ", ("like", "verb"), " this sort of ", ("thing", "noun"), ".") 248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 | Provides a sketching canvas using Fabric.js. Created by @andfanilo.
259 |
260 | python 261 | st_canvas(fill_color="rgba(255, 165, 0, 0.3)", stroke_width=stroke_width, stroke_color=stroke_color, background_color=bg_color, background_image=Image.open(bg_image) if bg_image else None, update_streamlit=realtime_update, height=150, drawing_mode=drawing_mode, point_display_radius=point_display_radius if drawing_mode == 'point' else 0, key="canvas",) 262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 | Add tags to your Streamlit apps. Created by @gagan3012.
273 |
274 | python 275 | st_tags(label='# Enter Keywords:', text='Press enter to add more', value=['Zero', 'One', 'Two'], suggestions=['five', 'six', 'seven', 'eight', 'nine', 'three', 'eleven', 'ten', 'four'], maxtags = 4, key='1') 276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 | Apply text mining on a dataframe. Created by @JohnSnowLabs.
287 |
288 | python 289 | nlu.load('sentiment').predict('I love NLU! <3') 290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 | A library with useful Streamlit extras. Created by @arnaudmiribel.
301 |
302 | python 303 | mention(label="An awesome Streamlit App", icon="streamlit", url="https://extras.streamlit.app",) 304 |
305 |
306 |
307 |
308 |
309 | ### Data elements
310 |
311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | Display a dataframe as an interactive table. 320 | 321 |
python 322 | st.dataframe(my_data_frame) 323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 | Display a data editor widget.
333 |
334 | python 335 | edited = st.data_editor(df, num_rows="dynamic") 336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 | Configure the display and editing behavior of dataframes and data editors.
346 |
347 | python 348 | st.column_config.NumberColumn("Price (in USD)", min_value=0, format="$%d") 349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 | Display a static table.
359 |
360 | python 361 | st.table(my_data_frame) 362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 | Display a metric in big bold font, with an optional indicator of how the metric changed.
371 |
372 | python 373 | st.metric("My metric", 42, 2) 374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | Display object or string as a pretty-printed JSON string.
383 |
384 | python 385 | st.json(my_dict) 386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 | Implementation of Ag-Grid component for Streamlit. Created by @PablocFonseca.
400 |
401 | python 402 | df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]}) 403 | grid_return = AgGrid(df, editable=True) 404 | 405 | new_df = grid_return['data'] 406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 | Streamlit Component for rendering Folium maps. Created by @randyzwitch.
417 |
418 | python 419 | m = folium.Map(location=[39.949610, -75.150282], zoom_start=16) 420 | folium.Marker([39.949610, -75.150282], popup="Liberty Bell", tooltip="Liberty Bell").add_to(m) 421 | 422 | st_data = st_folium(m, width=725) 423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 | Pandas profiling component for Streamlit. Created by @okld.
434 |
435 | python 436 | df = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv") 437 | pr = df.profile_report() 438 | 439 | st_profile_report(pr) 440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 | Get the coordinates of clicks on an image. Created by @blackary.
451 |
452 | python 453 | from streamlit_image_coordinates import streamlit_image_coordinates 454 | value = streamlit_image_coordinates("https://placekitten.com/200/300") 455 | 456 | st.write(value) 457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 | Make Plotly charts interactive!. Created by @null-jones.
468 |
469 | python 470 | from streamlit_plotly_events import plotly_events 471 | fig = px.line(x=[1], y=[1]) 472 | 473 | selected_points = plotly_events(fig) 474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 | A library with useful Streamlit extras. Created by @arnaudmiribel.
485 |
486 | python 487 | from streamlit_extras.metric_cards import style_metric_cards 488 | col3.metric(label="No Change", value=5000, delta=0) 489 | 490 | style_metric_cards() 491 |
492 |
493 |
494 |
495 |
496 |
497 | ### Chart elements
498 |
499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | Display an area chart. 509 | 510 |
python 511 | st.area_chart(my_data_frame) 512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 | Display a bar chart.
521 |
522 | python 523 | st.bar_chart(my_data_frame) 524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 | Display a line chart.
533 |
534 | python 535 | st.line_chart(my_data_frame) 536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 | Display a line chart.
545 |
546 | python 547 | st.scatter_chart(my_data_frame) 548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 | Display a map with points on it.
557 |
558 | python 559 | st.map(my_data_frame) 560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 | Display a matplotlib.pyplot figure.
569 |
570 | python 571 | st.pyplot(my_mpl_figure) 572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 | Display a chart using the Altair library.
581 |
582 | python 583 | st.altair_chart(my_altair_chart) 584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 | Display a chart using the Vega-Lite library.
593 |
594 | python 595 | st.vega_lite_chart(my_vega_lite_chart) 596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 | Display an interactive Plotly chart.
605 |
606 | python 607 | st.plotly_chart(my_plotly_chart) 608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 | Display an interactive Bokeh chart.
617 |
618 | python 619 | st.bokeh_chart(my_bokeh_chart) 620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 | Display a chart using the PyDeck library.
629 |
630 | python 631 | st.pydeck_chart(my_pydeck_chart) 632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 | Display a graph using the dagre-d3 library.
641 |
642 | python 643 | st.graphviz_chart(my_graphviz_spec) 644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 | A deceptively simple plotting library for Streamlit. Created by @tvst.
658 |
659 | python 660 | import plost 661 | plost.line_chart(my_dataframe, x='time', y='stock_value', color='stock_name',) 662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 | High dimensional Interactive Plotting. Created by @facebookresearch.
673 |
674 | python 675 | data = [{'dropout':0.1, 'lr': 0.001, 'loss': 10.0, 'optimizer': 'SGD'}, {'dropout':0.15, 'lr': 0.01, 'loss': 3.5, 'optimizer': 'Adam'}, {'dropout':0.3, 'lr': 0.1, 'loss': 4.5, 'optimizer': 'Adam'}] 676 | hip.Experiment.from_iterable(data).display() 677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 | High dimensional Interactive Plotting. Created by @andfanilo.
688 |
689 | python 690 | from streamlit_echarts import st_echarts 691 | st_echarts(options=options) 692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 | Streamlit Component for rendering Folium maps. Created by @randyzwitch.
703 |
704 | python 705 | m = folium.Map(location=[39.949610, -75.150282], zoom_start=16) 706 | st_data = st_folium(m, width=725) 707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 | spaCy building blocks and visualizers for Streamlit apps. Created by @explosion.
718 |
719 | python 720 | models = ["en_core_web_sm", "en_core_web_md"] 721 | spacy_streamlit.visualize(models, "Sundar Pichai is the CEO of Google.") 722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 | A Streamlit Graph Vis, based on react-grah-vis. Created by @ChrisDelClea.
733 |
734 | python 735 | from streamlit_agraph import agraph, Node, Edge, Config 736 | agraph(nodes=nodes, edges=edges, config=config) 737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 | Integrate Lottie animations inside your Streamlit app. Created by @andfanilo.
748 |
749 | python 750 | lottie_hello = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_V9t630.json") 751 | st_lottie(lottie_hello, key="hello") 752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 | Make Plotly charts interactive!. Created by @null-jones.
763 |
764 | python 765 | fig = px.line(x=[1], y=[1]) 766 | selected_points = plotly_events(fig) 767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 | A library with useful Streamlit extras. Created by @arnaudmiribel.
778 |
779 | python 780 | chart += get_annotations_chart(annotations=[("Mar 01, 2008", "Pretty good day for GOOG"), ("Dec 01, 2007", "Something's going wrong for GOOG & AAPL"), ("Nov 01, 2008", "Market starts again thanks to..."), ("Dec 01, 2009", "Small crash for GOOG after..."),],) 781 | st.altair_chart(chart, use_container_width=True) 782 |
783 |
784 |
785 |
786 |
787 |
788 | ### Input widgets
789 |
790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | Display a button widget. 800 | 801 |
python 802 | clicked = st.button("Click me") 803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 | Display a download button widget.
813 |
814 | python 815 | st.download_button("Download file", file) 816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 | Display a form submit button. For use with st.form
.
826 |
827 | python 828 | st.form_submit_button("Sign up") 829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 | Display a link button.
839 |
840 | python 841 | st.link_button("Go to gallery", url) 842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 | Display a link to another page in a multipage app.
852 |
853 | python 854 | st.page_link("app.py", label="Home", icon="🏠") 855 | st.page_link("pages/profile.py", label="My profile") 856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 | Display a checkbox widget.
866 |
867 | python 868 | selected = st.checkbox("I agree") 869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 | Display a color picker widget.
879 |
880 | python 881 | color = st.color_picker("Pick a color") 882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 | Display a rating or sentiment button group.
892 |
893 | python 894 | st.feedback("stars") 895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 | Display a multiselect widget. The multiselect widget starts as empty.
905 |
906 | python 907 | choices = st.multiselect("Buy", ["milk", "apples", "potatoes"]) 908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 | Display a pill-button selection widget.
918 |
919 | python 920 | st.pills("Tags", ["Sports", "AI", "Politics"]) 921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 | Display a radio button widget.
931 |
932 | python 933 | choice = st.radio("Pick one", ["cats", "dogs"]) 934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 | Display a segmented-button selection widget.
944 |
945 | python 946 | st.segmented_control("Filter", ["Open", "Closed", "All"]) 947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 | Display a select widget.
957 |
958 | python 959 | choice = st.selectbox("Pick one", ["cats", "dogs"]) 960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 | Display a slider widget to select items from a list.
970 |
971 | python 972 | size = st.select_slider("Pick a size", ["S", "M", "L"]) 973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 | Display a toggle widget.
983 |
984 | python 985 | activated = st.toggle("Activate") 986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 | Display a numeric input widget.
996 |
997 | python 998 | choice = st.number_input("Pick a number", 0, 10) 999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 | Display a slider widget.
1009 |
1010 | python 1011 | number = st.slider("Pick a number", 0, 100) 1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 | Display a date input widget.
1022 |
1023 | python 1024 | date = st.date_input("Your birthday") 1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 | Display a time input widget.
1035 |
1036 | python 1037 | time = st.time_input("Meeting time") 1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 | Display a chat input widget.
1048 |
1049 | python 1050 | prompt = st.chat_input("Say something") 1051 | if prompt: 1052 | st.write(f"The user has sent: {prompt}") 1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 | Display a multi-line text input widget.
1063 |
1064 | python 1065 | text = st.text_area("Text to translate") 1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 | Display a single-line text input widget.
1076 |
1077 | python 1078 | name = st.text_input("First name") 1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 | Display a widget that allows users to record with their microphone.
1089 |
1090 | python 1091 | speech = st.audio_input("Record a voice message") 1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 | Display a data editor widget.
1102 |
1103 | python 1104 | edited = st.data_editor(df, num_rows="dynamic") 1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 | Display a file uploader widget.
1115 |
1116 | python 1117 | data = st.file_uploader("Upload a CSV") 1118 |
1119 |
1120 |
1121 |
1122 |
1123 |
1124 |
1125 |
1126 |
1127 | Display a widget that allows users to upload images directly from a camera.
1128 |
1129 | python 1130 | image = st.camera_input("Take a picture") 1131 |
1132 |
1133 |
1134 |
1135 |
1136 |
1137 |
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 | Create a draggable and resizable dashboard in Streamlit. Created by @okls.
1145 |
1146 | python 1147 | from streamlit_elements import elements, mui, html 1148 | 1149 | with elements("new_element"): 1150 | mui.Typography("Hello world") 1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 | Add tags to your Streamlit apps. Created by @gagan3012.
1162 |
1163 | python 1164 | from streamlit_tags import st_tags 1165 | 1166 | st_tags(label='# Enter Keywords:', text='Press enter to add more', value=['Zero', 'One', 'Two'], 1167 | suggestions=['five', 'six', 'seven', 'eight', 'nine', 'three', 'eleven', 'ten', 'four'], maxtags = 4, key='1') 1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 | The simplest way to handle a progress bar in streamlit app. Created by @Wirg.
1179 |
1180 | python 1181 | from stqdm import stqdm 1182 | 1183 | for _ in stqdm(range(50)): 1184 | sleep(0.5) 1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 | Display a Timeline in Streamlit apps using TimelineJS. Created by @innerdoc.
1196 |
1197 | python 1198 | from streamlit_timeline import timeline 1199 | 1200 | with open('example.json', "r") as f: 1201 | timeline(f.read(), height=800) 1202 |
1203 |
1204 |
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 | Alternative for st.camera_input which returns the webcam images live. Created by @blackary.
1213 |
1214 | python 1215 | from camera_input_live import camera_input_live 1216 | 1217 | image = camera_input_live() 1218 | st.image(value) 1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 | Ace editor component for Streamlit. Created by @okld.
1230 |
1231 | python 1232 | from streamlit_ace import st_ace 1233 | 1234 | content = st_ace() 1235 | content 1236 |
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 | Streamlit Component for a Chatbot UI. Created by @AI-Yash.
1247 |
1248 | python 1249 | from streamlit_chat import message 1250 | 1251 | message("My message") 1252 | message("Hello bot!", is_user=True) # align's the message to the right 1253 |
1254 |
1255 |
1256 |
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 | Select a single item from a list of options in a menu. Created by @victoryhb.
1264 |
1265 | python 1266 | from streamlit_option_menu import option_menu 1267 | 1268 | option_menu("Main Menu", ["Home", 'Settings'], 1269 | icons=['house', 'gear'], menu_icon="cast", default_index=1) 1270 |
1271 |
1272 |
1273 |
1274 |
1275 |
1276 |
1277 |
1278 |
1279 |
1280 | A library with useful Streamlit extras. Created by @arnaudmiribel.
1281 |
1282 | python 1283 | from streamlit_extras.stoggle import stoggle 1284 | 1285 | stoggle( 1286 | "Click me!", """🥷 Surprise! Here's some additional content""",) 1287 |
1288 |
1289 |
1290 |
1291 |
1292 |
1293 | ### Media elements
1294 |
1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | Display an image or list of images. 1305 | 1306 |
python 1307 | st.image(numpy_array) 1308 | st.image(image_bytes) 1309 | st.image(file) 1310 | st.image("https://example.com/myimage.jpg") 1311 |
1312 |
1313 |
1314 |
1315 |
1316 |
1317 |
1318 |
1319 |
1320 | Display a logo in the upper-left corner of your app and its sidebar.
1321 |
1322 | python 1323 | st.logo("logo.jpg") 1324 |
1325 |
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 | Display an audio player.
1334 |
1335 | python 1336 | st.audio(numpy_array) 1337 | st.audio(audio_bytes) 1338 | st.audio(file) 1339 | st.audio("https://example.com/myaudio.mp3", format="audio/mp3") 1340 |
1341 |
1342 |
1343 |
1344 |
1345 |
1346 |
1347 |
1348 |
1349 | Display a video player.
1350 |
1351 | python 1352 | st.video(numpy_array) 1353 | st.video(video_bytes) 1354 | st.video(file) 1355 | st.video("https://example.com/myvideo.mp4", format="video/mp4") 1356 |
1357 |
1358 |
1359 |
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 |
1368 |
1369 | Handling and transmitting real-time video/audio streams with Streamlit. Created by @whitphx.
1370 |
1371 | python 1372 | from streamlit_webrtc import webrtc_streamer 1373 | 1374 | webrtc_streamer(key="sample") 1375 |
1376 |
1377 |
1378 |
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 | Provides a sketching canvas using Fabric.js. Created by @andfanilo.
1386 |
1387 | python 1388 | from streamlit_drawable_canvas import st_canvas 1389 | 1390 | st_canvas(fill_color="rgba(255, 165, 0, 0.3)", stroke_width=stroke_width, stroke_color=stroke_color, background_color=bg_color, background_image=Image.open(bg_image) if bg_image else None, update_streamlit=realtime_update, height=150, drawing_mode=drawing_mode, point_display_radius=point_display_radius if drawing_mode == 'point' else 0, key="canvas",) 1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 |
1400 |
1401 | Compare images with a slider using JuxtaposeJS. Created by @fcakyon.
1402 |
1403 | python 1404 | from streamlit_image_comparison import image_comparison 1405 | 1406 | image_comparison(img1="image1.jpg", img2="image2.jpg",) 1407 |
1408 |
1409 |
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 | A simple image cropper for Streamlit. Created by @turner-anderson.
1418 |
1419 | python 1420 | from streamlit_cropper import st_cropper 1421 | 1422 | st_cropper(img, realtime_update=realtime_update, box_color=box_color, aspect_ratio=aspect_ratio) 1423 |
1424 |
1425 |
1426 |
1427 |
1428 |
1429 |
1430 |
1431 |
1432 |
1433 | Get the coordinates of clicks on an image. Created by @blackary.
1434 |
1435 | python 1436 | from streamlit_image_coordinates import streamlit_image_coordinates 1437 | 1438 | streamlit_image_coordinates("https://placekitten.com/200/300") 1439 |
1440 |
1441 |
1442 |
1443 |
1444 |
1445 |
1446 |
1447 |
1448 |
1449 | Integrate Lottie animations inside your Streamlit app. Created by @andfanilo.
1450 |
1451 | python 1452 | lottie_hello = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_V9t630.json") 1453 | 1454 | st_lottie(lottie_hello, key="hello") 1455 |
1456 |
1457 |
1458 |
1459 |
1460 |
1461 | ### Layouts and containers
1462 |
1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1471 | 1472 | Insert containers laid out as side-by-side columns. 1473 | 1474 |
python 1475 | col1, col2 = st.columns(2) 1476 | col1.write("this is column 1") 1477 | col2.write("this is column 2") 1478 |
1479 |
1480 |
1481 |
1482 |
1483 |
1484 |
1485 |
1486 |
1487 | Insert a multi-element container.
1488 |
1489 | python 1490 | c = st.container() 1491 | st.write("This will show last") 1492 | c.write("This will show first") 1493 | c.write("This will show second") 1494 |
1495 |
1496 |
1497 |
1498 |
1499 |
1500 |
1501 |
1502 |
1503 | Insert a modal dialog that can rerun independently from the rest of the script.
1504 |
1505 | python 1506 | @st.dialog("Sign up") 1507 | def email_form(): 1508 | name = st.text_input("Name") 1509 | email = st.text_input("Email") 1510 |
1511 |
1512 |
1513 |
1514 |
1515 |
1516 |
1517 |
1518 |
1519 | Insert a single-element container.
1520 |
1521 | python 1522 | c = st.empty() 1523 | st.write("This will show last") 1524 | c.write("This will be replaced") 1525 | c.write("This will show first") 1526 |
1527 |
1528 |
1529 |
1530 |
1531 |
1532 |
1533 |
1534 |
1535 | Insert a multi-element container that can be expanded/collapsed.
1536 |
1537 | python 1538 | with st.expander("Open to see more"): 1539 | st.write("This is more content") 1540 |
1541 |
1542 |
1543 |
1544 |
1545 |
1546 |
1547 |
1548 |
1549 | Insert a multi-element popover container that can be opened/closed.
1550 |
1551 | python 1552 | with st.popover("Settings"): 1553 | st.checkbox("Show completed") 1554 |
1555 |
1556 |
1557 |
1558 |
1559 |
1560 |
1561 |
1562 |
1563 | Display items in a sidebar.
1564 |
1565 | python 1566 | st.sidebar.write("This lives in the sidebar") 1567 | st.sidebar.button("Click me!") 1568 |
1569 |
1570 |
1571 |
1572 |
1573 |
1574 |
1575 |
1576 |
1577 | Insert containers separated into tabs.
1578 |
1579 | python 1580 | tab1, tab2 = st.tabs(["Tab 1", "Tab2"]) 1581 | tab1.write("this is tab 1") 1582 | tab2.write("this is tab 2") 1583 |
1584 |
1585 |
1586 |
1587 |
1588 |
1589 |
1590 |
1591 |
1592 |
1593 |
1594 |
1595 |
1596 | Create a draggable and resizable dashboard in Streamlit. Created by @okls.
1597 |
1598 | python 1599 | from streamlit_elements import elements, mui, html 1600 | 1601 | with elements("new_element"): 1602 | mui.Typography("Hello world") 1603 |
1604 |
1605 |
1606 |
1607 |
1608 |
1609 |
1610 |
1611 |
1612 |
1613 | Auto-generate Streamlit UI from Pydantic Models and Dataclasses. Created by @lukasmasuch.
1614 |
1615 | python 1616 | import streamlit_pydantic as sp 1617 | 1618 | sp.pydantic_form(key="my_form", 1619 | model=ExampleModel) 1620 |
1621 |
1622 |
1623 |
1624 |
1625 |
1626 |
1627 |
1628 |
1629 |
1630 | An experimental version of Streamlit Multi-Page Apps. Created by @blackary.
1631 |
1632 | python 1633 | from st_pages import Page, show_pages, add_page_title 1634 | 1635 | show_pages([ Page("streamlit_app.py", "Home", "🏠"), 1636 | Page("other_pages/page2.py", "Page 2", ":books:"), ]) 1637 |
1638 |
1639 |
1640 |
1641 |
1642 |
1643 | ### Chat elements
1644 |
1645 | 1646 | 1647 | Streamlit provides a few commands to help you build conversational apps. These chat elements are designed to be used in conjunction with each other, but you can also use them separately. 1648 | 1649 |
st.chat_message
lets you insert a chat message container into the app so you can display messages from the user or the app. Chat containers can contain other Streamlit elements, including charts, tables, text, and more. st.chat_input
lets you display a chat input widget so the user can type in a message.
1650 |
1651 |
1652 |
1653 |
1654 |
1655 |
1656 |
1657 |
1658 | Display a chat input widget.
1659 |
1660 | python 1661 | prompt = st.chat_input("Say something") 1662 | if prompt: 1663 | st.write(f"The user has sent: {prompt}") 1664 |
1665 |
1666 |
1667 |
1668 |
1669 |
1670 |
1671 |
1672 |
1673 | Insert a chat message container.
1674 |
1675 | python 1676 | import numpy as np 1677 | with st.chat_message("user"): 1678 | st.write("Hello 👋") 1679 | st.line_chart(np.random.randn(30, 3)) 1680 |
1681 |
1682 |
1683 |
1684 |
1685 |
1686 |
1687 |
1688 |
1689 | Display output of long-running tasks in a container.
1690 |
1691 | python 1692 | with st.status('Running'): 1693 | do_something_slow() 1694 |
1695 |
1696 |
1697 |
1698 |
1699 |
1700 |
1701 | Write generators or streams to the app with a typewriter effect.
1702 |
1703 | python 1704 | st.write_stream(my_generator) 1705 | st.write_stream(my_llm_stream) 1706 |
1707 |
1708 |
1709 |
1710 |
1711 | ### Status elements
1712 |
1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | Display a progress bar. 1723 | 1724 |
python 1725 | for i in range(101): 1726 | st.progress(i) 1727 | do_something_slow() 1728 |
1729 |
1730 |
1731 |
1732 |
1733 |
1734 |
1735 |
1736 |
1737 | Temporarily displays a message while executing a block of code.
1738 |
1739 | python 1740 | with st.spinner("Please wait..."): 1741 | do_something_slow() 1742 |
1743 |
1744 |
1745 |
1746 |
1747 |
1748 |
1749 |
1750 |
1751 | Display output of long-running tasks in a container.
1752 |
1753 | python 1754 | with st.status('Running'): 1755 | do_something_slow() 1756 |
1757 |
1758 |
1759 |
1760 |
1761 |
1762 |
1763 |
1764 |
1765 | Briefly displays a toast message in the bottom-right corner.
1766 |
1767 | python 1768 | st.toast('Butter!', icon='🧈') 1769 |
1770 |
1771 |
1772 |
1773 |
1774 |
1775 |
1776 |
1777 |
1778 | Display celebratory balloons!
1779 |
1780 | python 1781 | do_something() 1782 | 1783 | # Celebrate when all done! 1784 | st.balloons() 1785 |
1786 |
1787 |
1788 |
1789 |
1790 |
1791 |
1792 |
1793 |
1794 | Display celebratory snowflakes!
1795 |
1796 | python 1797 | do_something() 1798 | 1799 | # Celebrate when all done! 1800 | st.snow() 1801 |
1802 |
1803 |
1804 |
1805 |
1806 |
1807 |
1808 |
1809 |
1810 | Display a success message.
1811 |
1812 | python 1813 | st.success("Match found!") 1814 |
1815 |
1816 |
1817 |
1818 |
1819 |
1820 |
1821 |
1822 |
1823 | Display an informational message.
1824 |
1825 | python 1826 | st.info("Dataset is updated every day at midnight.") 1827 |
1828 |
1829 |
1830 |
1831 |
1832 |
1833 |
1834 |
1835 |
1836 | Display warning message.
1837 |
1838 | python 1839 | st.warning("Unable to fetch image. Skipping...") 1840 |
1841 |
1842 |
1843 |
1844 |
1845 |
1846 |
1847 |
1848 |
1849 | Display error message.
1850 |
1851 | python 1852 | st.error("We encountered an error") 1853 |
1854 |
1855 |
1856 |
1857 |
1858 |
1859 |
1860 |
1861 |
1862 | Display an exception.
1863 |
1864 | python 1865 | e = RuntimeError("This is an exception of type RuntimeError") 1866 | st.exception(e) 1867 |
1868 |
1869 |
1870 |
1871 |
1872 |
1873 |
1874 |
1875 |
1876 |
1877 |
1878 |
1879 |
1880 |
1881 | The simplest way to handle a progress bar in streamlit app. Created by @Wirg.
1882 |
1883 | python 1884 | from stqdm import stqdm 1885 | 1886 | for _ in stqdm(range(50)): 1887 | sleep(0.5) 1888 |
1889 |
1890 |
1891 |
1892 |
1893 |
1894 |
1895 |
1896 |
1897 |
1898 | A custom notification box with the ability to close it out. Created by @Socvest.
1899 |
1900 | python 1901 | from streamlit_custom_notification_box import custom_notification_box 1902 | 1903 | styles = {'material-icons':{'color': 'red'}, 'text-icon-link-close-container': {'box-shadow': '#3896de 0px 4px'}, 'notification-text': {'':''}, 'close-button':{'':''}, 'link':{'':''}} 1904 | custom_notification_box(icon='info', textDisplay='We are almost done with your registration...', externalLink='more info', url='#', styles=styles, key="foo") 1905 |
1906 |
1907 |
1908 |
1909 |
1910 |
1911 |
1912 |
1913 |
1914 |
1915 | A library with useful Streamlit extras. Created by @arnaudmiribel.
1916 |
1917 | python 1918 | from streamlit_extras.let_it_rain import rain 1919 | 1920 | rain(emoji="🎈", font_size=54, 1921 | falling_speed=5, animation_length="infinite",) 1922 |
1923 |
1924 |
1925 |
1926 |
1927 |
1928 | ## App logic and configuration
1929 |
1930 | ### Authentication and user info
1931 |
1932 | 1933 | 1934 | 1935 | 1936 | 1937 | 1938 | 1939 |
st.login()
starts an authentication flow with an identity provider.
1940 |
1941 | python 1942 | st.login() 1943 |
1944 |
1945 |
1946 |
1947 |
1948 |
1949 |
1950 | st.logout()
removes a user's identity information.
1951 |
1952 | python 1953 | st.logout() 1954 |
1955 |
1956 |
1957 |
1958 |
1959 |
1960 |
1961 | st.user
returns information about a logged-in user.
1962 |
1963 | python 1964 | if st.user.is_logged_in: 1965 | st.write(f"Welcome back, {st.user.name}!") 1966 |
1967 |
1968 |
1969 |
1970 |
1971 | ### Navigation and pages
1972 |
1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | 1980 | 1981 | 1982 | 1983 | Configure the available pages in a multipage app. 1984 | 1985 |
python 1986 | st.navigation({ 1987 | "Your account" : [log_out, settings], 1988 | "Reports" : [overview, usage], 1989 | "Tools" : [search] 1990 | }) 1991 |
1992 |
1993 |
1994 |
1995 |
1996 |
1997 |
1998 |
1999 |
2000 |
2001 | Define a page in a multipage app.
2002 |
2003 | python 2004 | home = st.Page( 2005 | "home.py", 2006 | title="Home", 2007 | icon=":material/home:" 2008 | ) 2009 |
2010 |
2011 |
2012 |
2013 |
2014 |
2015 |
2016 |
2017 |
2018 |
2019 | Display a link to another page in a multipage app.
2020 |
2021 | python 2022 | st.page_link("app.py", label="Home", icon="🏠") 2023 | st.page_link("pages/profile.py", label="My profile") 2024 |
2025 |
2026 |
2027 |
2028 |
2029 |
2030 |
2031 |
2032 | Programmatically navigates to a specified page.
2033 |
2034 | python 2035 | st.switch_page("pages/my_page.py") 2036 |
2037 |
2038 |
2039 |
2040 |
2041 |
2042 | ### Execution flow
2043 |
2044 | 2045 | 2046 | 2047 | 2048 | 2049 | 2050 | 2051 | 2052 | 2053 | Insert a modal dialog that can rerun independently from the rest of the script. 2054 | 2055 |
python 2056 | @st.dialog("Sign up") 2057 | def email_form(): 2058 | name = st.text_input("Name") 2059 | email = st.text_input("Email") 2060 |
2061 |
2062 |
2063 |
2064 |
2065 |
2066 |
2067 | Create a form that batches elements together with a “Submit" button.
2068 |
2069 | python 2070 | with st.form(key='my_form'): 2071 | name = st.text_input("Name") 2072 | email = st.text_input("Email") 2073 | st.form_submit_button("Sign up") 2074 |
2075 |
2076 |
2077 |
2078 |
2079 |
2080 |
2081 | Define a fragment to rerun independently from the rest of the script.
2082 |
2083 | python 2084 | @st.fragment(run_every="10s") 2085 | def fragment(): 2086 | df = get_data() 2087 | st.line_chart(df) 2088 |
2089 |
2090 |
2091 |
2092 |
2093 |
2094 |
2095 | Rerun the script immediately.
2096 |
2097 | python 2098 | st.rerun() 2099 |
2100 |
2101 |
2102 |
2103 |
2104 |
2105 |
2106 | Stops execution immediately.
2107 |
2108 | python 2109 | st.stop() 2110 |
2111 |
2112 |
2113 |
2114 |
2115 |
2116 |
2117 |
2118 |
2119 |
2120 |
2121 |
2122 |
2123 | Force a refresh without tying up a script. Created by @kmcgrady.
2124 |
2125 | python 2126 | from streamlit_autorefresh import st_autorefresh 2127 | 2128 | st_autorefresh(interval=2000, limit=100, 2129 | key="fizzbuzzcounter") 2130 |
2131 |
2132 |
2133 |
2134 |
2135 |
2136 |
2137 |
2138 |
2139 |
2140 | Auto-generate Streamlit UI from Pydantic Models and Dataclasses. Created by @lukasmasuch.
2141 |
2142 | python 2143 | import streamlit_pydantic as sp 2144 | 2145 | sp.pydantic_form(key="my_form", 2146 | model=ExampleModel) 2147 |
2148 |
2149 |
2150 |
2151 |
2152 |
2153 |
2154 |
2155 |
2156 |
2157 | An experimental version of Streamlit Multi-Page Apps. Created by @blackary.
2158 |
2159 | python 2160 | from st_pages import Page, show_pages, add_page_title 2161 | 2162 | show_pages([ Page("streamlit_app.py", "Home", "🏠"), 2163 | Page("other_pages/page2.py", "Page 2", ":books:"), ]) 2164 |
2165 |
2166 |
2167 |
2168 |
2169 |
2170 | ### Caching and state
2171 |
2172 | 2173 | 2174 | 2175 | 2176 | 2177 | 2178 | 2179 | Function decorator to cache functions that return data (e.g. dataframe transforms, database queries, ML inference). 2180 | 2181 |
python 2182 | @st.cache_data 2183 | def long_function(param1, param2): 2184 | # Perform expensive computation here or 2185 | # fetch data from the web here 2186 | return data 2187 |
2188 |
2189 |
2190 |
2191 |
2192 |
2193 |
2194 |
2195 | Function decorator to cache functions that return global resources (e.g. database connections, ML models).
2196 |
2197 | python 2198 | @st.cache_resource 2199 | def init_model(): 2200 | # Return a global resource here 2201 | return pipeline( 2202 | "sentiment-analysis", 2203 | model="distilbert-base-uncased-finetuned-sst-2-english" 2204 | ) 2205 |
2206 |
2207 |
2208 |
2209 |
2210 |
2211 |
2212 |
2213 | Session state is a way to share variables between reruns, for each user session.
2214 |
2215 | python 2216 | st.session_state['key'] = value 2217 |
2218 |
2219 |
2220 |
2221 |
2222 |
2223 |
2224 |
2225 | Get, set, or clear the query parameters that are shown in the browser's URL bar.
2226 |
2227 | python 2228 | st.query_params[key] = value 2229 | st.query_params.clear() 2230 |
2231 |
2232 |
2233 |
2234 |
2235 |
2236 |
2237 | st.context
provides a read-only interface to access cookies, headers, locale, and other browser-session information.
2238 |
2239 | python 2240 | st.context.cookies 2241 | st.context.headers 2242 |
2243 |
2244 |
2245 |
2246 |
2247 |
2248 | ### Connections and databases
2249 |
2250 | #### Setup your connection
2251 |
2252 |
2253 |
2254 |
2255 |
2256 |
2257 |
2258 |
2259 | Connect to a data source or API
2260 |
2261 | python 2262 | conn = st.connection('pets_db', type='sql') 2263 | pet_owners = conn.query('select * from pet_owners') 2264 | st.dataframe(pet_owners) 2265 |
2266 |
2267 |
2268 |
2269 |
2270 | #### Built-in connections
2271 |
2272 |
2273 |
2274 |
2275 |
2276 |
2277 |
2278 |
2279 |
2280 | A connection to Snowflake.
2281 |
2282 | python 2283 | conn = st.connection('snowflake') 2284 |
2285 |
2286 |
2287 |
2288 |
2289 |
2290 |
2291 |
2292 |
2293 |
2294 | A connection to a SQL database using SQLAlchemy.
2295 |
2296 | python 2297 | conn = st.connection('sql') 2298 |
2299 |
2300 |
2301 |
2302 |
2303 | #### Build your own connections
2304 |
2305 |
2306 |
2307 |
2308 |
2309 |
2310 | Build your own connection with BaseConnection
.
2311 |
2312 | python 2313 | class MyConnection(BaseConnection[myconn.MyConnection]): 2314 | def _connect(self, **kwargs) -> MyConnection: 2315 | return myconn.connect(**self._secrets, **kwargs) 2316 | def query(self, query): 2317 | return self._instance.query(query) 2318 |
2319 |
2320 |
2321 |
2322 |
2323 |
2324 | #### Secrets management
2325 |
2326 |
2327 |
2328 |
2329 |
2330 |
2331 |
2332 | Access secrets from a local TOML file.
2333 |
2334 | python 2335 | key = st.secrets["OpenAI_key"] 2336 |
2337 |
2338 |
2339 |
2340 |
2341 |
2342 |
2343 | Save your secrets in a per-project or per-profile TOML file.
2344 |
2345 | python 2346 | OpenAI_key = "<YOUR_SECRET_KEY>" 2347 |
2348 |
2349 |
2350 |
2351 |
2352 |
2353 |
2354 |
2355 |
2356 |
2357 |
2358 |
2359 |
2360 |
2361 | A secure authentication module to validate user credentials. Created by @mkhorasani.
2362 |
2363 | python 2364 | import streamlit_authenticator as stauth 2365 | 2366 | authenticator = stauth.Authenticate( config['credentials'], config['cookie']['name'], 2367 | config['cookie']['key'], config['cookie']['expiry_days'], config['preauthorized']) 2368 |
2369 |
2370 |
2371 |
2372 |
2373 |
2374 |
2375 |
2376 |
2377 |
2378 | A simple synchronous way of accessing localStorage from your app. Created by @gagangoku.
2379 |
2380 | python 2381 | from streamlit_ws_localstorage import injectWebsocketCode 2382 | 2383 | ret = conn.setLocalStorageVal(key='k1', val='v1') 2384 | st.write('ret: ' + ret) 2385 |
2386 |
2387 |
2388 |
2389 |
2390 |
2391 |
2392 |
2393 |
2394 |
2395 | The fastest way to provide comprehensive login inside Streamlit. Created by @conradbez.
2396 |
2397 | python 2398 | from auth0_component import login_button 2399 | 2400 | user_info = login_button(clientId, domain = domain) 2401 | st.write(user_info) 2402 |
2403 |
2404 |
2405 |
2406 |
2407 |
2408 | ### Custom Components
2409 |
2410 | 2411 | 2412 | 2413 | 2414 | 2415 | 2416 | 2417 | 2418 | Create and register a custom component. 2419 | 2420 |
python 2421 | from st.components.v1 import declare_component 2422 | declare_component( 2423 | "custom_slider", 2424 | "/frontend", 2425 | ) 2426 |
2427 |
2428 |
2429 |
2430 |
2431 |
2432 |
2433 |
2434 | Display an HTML string in an iframe.
2435 |
2436 | python 2437 | from st.components.v1 import html 2438 | html( 2439 | "<p>Foo bar.</p>" 2440 | ) 2441 |
2442 |
2443 |
2444 |
2445 |
2446 |
2447 |
2448 |
2449 | Load a remote URL in an iframe.
2450 |
2451 | python 2452 | from st.components.v1 import iframe 2453 | iframe( 2454 | "docs.streamlit.io" 2455 | ) 2456 |
2457 |
2458 |
2459 |
2460 |
2461 |
2462 | ### Configuration
2463 |
2464 | 2465 | 2466 | 2467 | 2468 | 2469 | 2470 | 2471 | Configures the default settings for your app. 2472 | 2473 |
2474 | your-project/ 2475 | ├── .streamlit/ 2476 | │ └── config.toml 2477 | └── your_app.py 2478 |
2479 |
2480 |
2481 |
2482 |
2483 |
2484 |
2485 | Retrieve a single configuration option.
2486 |
2487 | python 2488 | st.get_option("theme.primaryColor") 2489 |
2490 |
2491 |
2492 |
2493 |
2494 |
2495 |
2496 | Set a single configuration option. (This is very limited.)
2497 |
2498 | python 2499 | st.set_option("deprecation.showPyplotGlobalUse", False) 2500 |
2501 |
2502 |
2503 |
2504 |
2505 |
2506 |
2507 | Configures the default settings of the page.
2508 |
2509 | python 2510 | st.set_page_config( 2511 | page_title="My app", 2512 | page_icon=":shark:", 2513 | ) 2514 |
2515 |
2516 |
2517 |
2518 |
2519 | ## Developer tools
2520 |
2521 | ### App testing
2522 |
2523 | 2524 | 2525 | 2526 | 2527 | 2528 | 2529 | 2530 | 2531 |
st.testing.v1.AppTest
simulates a running Streamlit app for testing.
2532 |
2533 | python 2534 | from streamlit.testing.v1 import AppTest 2535 | 2536 | at = AppTest.from_file("streamlit_app.py") 2537 | at.secrets["WORD"] = "Foobar" 2538 | at.run() 2539 | assert not at.exception 2540 | 2541 | at.text_input("word").input("Bazbat").run() 2542 | assert at.warning[0].value == "Try again." 2543 |
2544 |
2545 |
2546 |
2547 |
2548 |
2549 |
2550 |
2551 | st.testing.v1.AppTest.from_file
initializes a simulated app from a file.
2552 |
2553 | python 2554 | from streamlit.testing.v1 import AppTest 2555 | 2556 | at = AppTest.from_file("streamlit_app.py") 2557 | at.run() 2558 |
2559 |
2560 |
2561 |
2562 |
2563 |
2564 |
2565 |
2566 | st.testing.v1.AppTest.from_string
initializes a simulated app from a string.
2567 |
2568 | python 2569 | from streamlit.testing.v1 import AppTest 2570 | 2571 | at = AppTest.from_string(app_script_as_string) 2572 | at.run() 2573 |
2574 |
2575 |
2576 |
2577 |
2578 |
2579 |
2580 |
2581 | st.testing.v1.AppTest.from_function
initializes a simulated app from a function.
2582 |
2583 | python 2584 | from streamlit.testing.v1 import AppTest 2585 | 2586 | at = AppTest.from_function(app_script_as_callable) 2587 | at.run() 2588 |
2589 |
2590 |
2591 |
2592 |
2593 |
2594 |
2595 |
2596 | A representation of container elements, including:
2597 |
2598 | - st.chat_message
2599 | - st.columns
2600 | - st.sidebar
2601 | - st.tabs
2602 | - The main body of the app.
2603 |
2604 | python 2605 | # at.sidebar returns a Block 2606 | at.sidebar.button[0].click().run() 2607 | assert not at.exception 2608 |
2609 |
2610 |
2611 |
2612 |
2613 |
2614 |
2615 |
2616 | The base class for representation of all elements, including:
2617 |
2618 | - st.title
2619 | - st.header
2620 | - st.markdown
2621 | - st.dataframe
2622 |
2623 | python 2624 | # at.title returns a sequence of Title 2625 | # Title inherits from Element 2626 | assert at.title[0].value == "My awesome app" 2627 |
2628 |
2629 |
2630 |
2631 |
2632 |
2633 |
2634 |
2635 | A representation of st.button
and st.form_submit_button
.
2636 |
2637 | python 2638 | at.button[0].click().run() 2639 |
2640 |
2641 |
2642 |
2643 |
2644 |
2645 |
2646 |
2647 | A representation of st.chat_input
.
2648 |
2649 | python 2650 | at.chat_input[0].set_value("What is Streamlit?").run() 2651 |
2652 |
2653 |
2654 |
2655 |
2656 |
2657 |
2658 |
2659 | A representation of st.checkbox
.
2660 |
2661 | python 2662 | at.checkbox[0].check().run() 2663 |
2664 |
2665 |
2666 |
2667 |
2668 |
2669 |
2670 |
2671 | A representation of st.color_picker
.
2672 |
2673 | python 2674 | at.color_picker[0].pick("#FF4B4B").run() 2675 |
2676 |
2677 |
2678 |
2679 |
2680 |
2681 |
2682 |
2683 | A representation of st.date_input
.
2684 |
2685 | python 2686 | release_date = datetime.date(2023, 10, 26) 2687 | at.date_input[0].set_value(release_date).run() 2688 |
2689 |
2690 |
2691 |
2692 |
2693 |
2694 |
2695 |
2696 | A representation of st.multiselect
.
2697 |
2698 | python 2699 | at.multiselect[0].select("New York").run() 2700 |
2701 |
2702 |
2703 |
2704 |
2705 |
2706 |
2707 |
2708 | A representation of st.number_input
.
2709 |
2710 | python 2711 | at.number_input[0].increment().run() 2712 |
2713 |
2714 |
2715 |
2716 |
2717 |
2718 |
2719 |
2720 | A representation of st.radio
.
2721 |
2722 | python 2723 | at.radio[0].set_value("New York").run() 2724 |
2725 |
2726 |
2727 |
2728 |
2729 |
2730 |
2731 |
2732 | A representation of st.select_slider
.
2733 |
2734 | python 2735 | at.select_slider[0].set_range("A","C").run() 2736 |
2737 |
2738 |
2739 |
2740 |
2741 |
2742 |
2743 |
2744 | A representation of st.selectbox
.
2745 |
2746 | python 2747 | at.selectbox[0].select("New York").run() 2748 |
2749 |
2750 |
2751 |
2752 |
2753 |
2754 |
2755 |
2756 | A representation of st.slider
.
2757 |
2758 | python 2759 | at.slider[0].set_range(2,5).run() 2760 |
2761 |
2762 |
2763 |
2764 |
2765 |
2766 |
2767 |
2768 | A representation of st.text_area
.
2769 |
2770 | python 2771 | at.text_area[0].input("Streamlit is awesome!").run() 2772 |
2773 |
2774 |
2775 |
2776 |
2777 |
2778 |
2779 |
2780 | A representation of st.text_input
.
2781 |
2782 | python 2783 | at.text_input[0].input("Streamlit").run() 2784 |
2785 |
2786 |
2787 |
2788 |
2789 |
2790 |
2791 |
2792 | A representation of st.time_input
.
2793 |
2794 | python 2795 | at.time_input[0].increment().run() 2796 |
2797 |
2798 |
2799 |
2800 |
2801 |
2802 |
2803 |
2804 | A representation of st.toggle
.
2805 |
2806 | python 2807 | at.toggle[0].set_value("True").run() 2808 |
2809 |
2810 |
2811 |
2812 |
2813 |
2814 |
2815 |
2816 |
2817 |
2818 |
2819 |
2820 |
2821 |
2822 | Pandas profiling component for Streamlit. Created by @okld.
2823 |
2824 | python 2825 | df = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv") 2826 | pr = df.profile_report() 2827 | 2828 | st_profile_report(pr) 2829 |
2830 |
2831 |
2832 |
2833 |
2834 |
2835 |
2836 |
2837 |
2838 |
2839 | Ace editor component for Streamlit. Created by @okld.
2840 |
2841 | python 2842 | from streamlit_ace import st_ace 2843 | 2844 | content = st_ace() 2845 | content 2846 |
2847 |
2848 |
2849 |
2850 |
2851 |
2852 |
2853 |
2854 |
2855 |
2856 | Track & visualize user interactions with your streamlit app. Created by @jrieke.
2857 |
2858 | python 2859 | import streamlit_analytics 2860 | 2861 | with streamlit_analytics.track(): 2862 | st.text_input("Write something") 2863 |
2864 |
2865 |
2866 |
2867 |
2868 |
1 | --- 2 | title: Caching and state 3 | slug: /develop/api-reference/caching-and-state 4 | --- 5 | 6 | # Caching and state 7 | 8 | Optimize performance and add statefulness to your app! 9 | 10 | ## Caching 11 | 12 | Streamlit provides powerful cache primitives for data and global resources. They allow your app to stay performant even when loading data from the web, manipulating large datasets, or performing expensive computations. 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | Function decorator to cache functions that return data (e.g. dataframe transforms, database queries, ML inference). 21 | 22 |python 23 | @st.cache_data 24 | def long_function(param1, param2): 25 | # Perform expensive computation here or 26 | # fetch data from the web here 27 | return data 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Function decorator to cache functions that return global resources (e.g. database connections, ML models).
37 |
38 | python 39 | @st.cache_resource 40 | def init_model(): 41 | # Return a global resource here 42 | return pipeline( 43 | "sentiment-analysis", 44 | model="distilbert-base-uncased-finetuned-sst-2-english" 45 | ) 46 |
47 |
48 |
49 |
50 |
51 |
52 | ## Browser and server state
53 |
54 | Streamlit re-executes your script with each user interaction. Widgets have built-in statefulness between reruns, but Session State lets you do more!
55 |
56 |
57 |
58 |
59 |
60 |
61 | st.context
provides a read-only interface to access cookies, headers, locale, and other browser-session information.
62 |
63 | python 64 | st.context.cookies 65 | st.context.headers 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Save data between reruns and across pages.
74 |
75 | python 76 | st.session_state["foo"] = "bar" 77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Get, set, or clear the query parameters that are shown in the browser's URL bar.
85 |
86 | python 87 | st.query_params[key] = value 88 | st.query_params.clear() 89 |
90 |
91 |
92 |
93 |
94 |
95 | ## Deprecated commands
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | Get query parameters that are shown in the browser's URL bar.
104 |
105 | python 106 | param_dict = st.experimental_get_query_params() 107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | Set query parameters that are shown in the browser's URL bar.
115 |
116 | python 117 | st.experimental_set_query_params( 118 | {"show_all"=True, "selected"=["asia", "america"]} 119 | ) 120 |
121 |
122 |
123 |
124 |
1 | ---
2 | title: st.cache_data
3 | slug: /develop/api-reference/caching-and-state/st.cache_data
4 | description: st.cache_data is used to cache functions that return data (e.g. dataframe transforms, database queries, ML inference).
5 | ---
6 |
7 |
8 |
9 | This page only contains information on the st.cache_data
API. For a deeper dive into caching and how to use it, check out Caching.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | st.cache_data
implicitly uses the pickle
module, which is known to be insecure. Anything your cached function returns is pickled and stored, then unpickled on retrieval. Ensure your cached functions return trusted values because it is possible to construct malicious pickle data that will execute arbitrary code during unpickling. Never load data that could have come from an untrusted source in an unsafe mode or that could have been tampered with. Only load data you trust.
18 |
19 |
20 |
21 |
22 |
23 | #### Example
24 |
25 | In the example below, pressing the "Clear All" button will clear memoized values from all functions decorated with @st.cache_data
.
26 |
27 | python 28 | import streamlit as st 29 | 30 | @st.cache_data 31 | def square(x): 32 | return x**2 33 | 34 | @st.cache_data 35 | def cube(x): 36 | return x**3 37 | 38 | if st.button("Clear All"): 39 | # Clear values from *all* all in-memory and on-disk data caches: 40 | # i.e. clear values from both square and cube 41 | st.cache_data.clear() 42 |
43 |
44 |
45 |
46 | ## Using Streamlit commands in cached functions
47 |
48 | ### Static elements
49 |
50 | Since version 1.16.0, cached functions can contain Streamlit commands! For example, you can do this:
51 |
52 | python 53 | @st.cache_data 54 | def get_api_data(): 55 | data = api.get(...) 56 | st.success("Fetched data from API!") # 👈 Show a success message 57 | return data 58 |
59 |
60 | As we know, Streamlit only runs this function if it hasn’t been cached before. On this first run, the st.success
message will appear in the app. But what happens on subsequent runs? It still shows up! Streamlit realizes that there is an st.
command inside the cached function, saves it during the first run, and replays it on subsequent runs. Replaying static elements works for both caching decorators.
61 |
62 | You can also use this functionality to cache entire parts of your UI:
63 |
64 | python 65 | @st.cache_data 66 | def show_data(): 67 | st.header("Data analysis") 68 | data = api.get(...) 69 | st.success("Fetched data from API!") 70 | st.write("Here is a plot of the data:") 71 | st.line_chart(data) 72 | st.write("And here is the raw data:") 73 | st.dataframe(data) 74 |
75 |
76 | ### Input widgets
77 |
78 | You can also use interactive input widgets like st.slider
or st.text_input
in cached functions. Widget replay is an experimental feature at the moment. To enable it, you need to set the experimental_allow_widgets
parameter:
79 |
80 | python 81 | @st.cache_data(experimental_allow_widgets=True) # 👈 Set the parameter 82 | def get_data(): 83 | num_rows = st.slider("Number of rows to get") # 👈 Add a slider 84 | data = api.get(..., num_rows) 85 | return data 86 |
87 |
88 | Streamlit treats the slider like an additional input parameter to the cached function. If you change the slider position, Streamlit will see if it has already cached the function for this slider value. If yes, it will return the cached value. If not, it will rerun the function using the new slider value.
89 |
90 | Using widgets in cached functions is extremely powerful because it lets you cache entire parts of your app. But it can be dangerous! Since Streamlit treats the widget value as an additional input parameter, it can easily lead to excessive memory usage. Imagine your cached function has five sliders and returns a 100 MB DataFrame. Then we’ll add 100 MB to the cache for every permutation of these five slider values – even if the sliders do not influence the returned data! These additions can make your cache explode very quickly. Please be aware of this limitation if you use widgets in cached functions. We recommend using this feature only for isolated parts of your UI where the widgets directly influence the cached return value.
91 |
92 |
93 |
94 | Support for widgets in cached functions is currently experimental. We may change or remove it anytime without warning. Please use it with care!
95 |
96 |
97 |
98 |
99 | Two widgets are currently not supported in cached functions: st.file_uploader
and st.camera_input
. We may support them in the future. Feel free to open a GitHub issue if you need them!
100 |
101 |
1 | ---
2 | title: st.cache_resource
3 | slug: /develop/api-reference/caching-and-state/st.cache_resource
4 | description: st.cache_resource is used to cache functions that return shared global resources (e.g. database connections, ML models).
5 | ---
6 |
7 |
8 |
9 | This page only contains information on the st.cache_resource
API. For a deeper dive into caching and how to use it, check out Caching.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | #### Example
18 |
19 | In the example below, pressing the "Clear All" button will clear all cache_resource caches. i.e. Clears cached global resources from all functions decorated with @st.cache_resource
.
20 |
21 | python 22 | import streamlit as st 23 | from transformers import BertModel 24 | 25 | @st.cache_resource 26 | def get_database_session(url): 27 | # Create a database session object that points to the URL. 28 | return session 29 | 30 | @st.cache_resource 31 | def get_model(model_type): 32 | # Create a model of the specified type. 33 | return BertModel.from_pretrained(model_type) 34 | 35 | if st.button("Clear All"): 36 | # Clears all st.cache_resource caches: 37 | st.cache_resource.clear() 38 |
39 |
40 |
41 |
42 | ## Using Streamlit commands in cached functions
43 |
44 | ### Static elements
45 |
46 | Since version 1.16.0, cached functions can contain Streamlit commands! For example, you can do this:
47 |
48 | python 49 | from transformers import pipeline 50 | 51 | @st.cache_resource 52 | def load_model(): 53 | model = pipeline("sentiment-analysis") 54 | st.success("Loaded NLP model from Hugging Face!") # 👈 Show a success message 55 | return model 56 |
57 |
58 | As we know, Streamlit only runs this function if it hasn’t been cached before. On this first run, the st.success
message will appear in the app. But what happens on subsequent runs? It still shows up! Streamlit realizes that there is an st.
command inside the cached function, saves it during the first run, and replays it on subsequent runs. Replaying static elements works for both caching decorators.
59 |
60 | You can also use this functionality to cache entire parts of your UI:
61 |
62 | python 63 | @st.cache_resource 64 | def load_model(): 65 | st.header("Data analysis") 66 | model = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT) 67 | st.success("Loaded model!") 68 | st.write("Turning on evaluation mode...") 69 | model.eval() 70 | st.write("Here's the model:") 71 | return model 72 |
73 |
74 | ### Input widgets
75 |
76 | You can also use interactive input widgets like st.slider
or st.text_input
in cached functions. Widget replay is an experimental feature at the moment. To enable it, you need to set the experimental_allow_widgets
parameter:
77 |
78 | python 79 | @st.cache_resource(experimental_allow_widgets=True) # 👈 Set the parameter 80 | def load_model(): 81 | pretrained = st.checkbox("Use pre-trained model:") # 👈 Add a checkbox 82 | model = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT, pretrained=pretrained) 83 | return model 84 |
85 |
86 | Streamlit treats the checkbox like an additional input parameter to the cached function. If you uncheck it, Streamlit will see if it has already cached the function for this checkbox state. If yes, it will return the cached value. If not, it will rerun the function using the new slider value.
87 |
88 | Using widgets in cached functions is extremely powerful because it lets you cache entire parts of your app. But it can be dangerous! Since Streamlit treats the widget value as an additional input parameter, it can easily lead to excessive memory usage. Imagine your cached function has five sliders and returns a 100 MB DataFrame. Then we’ll add 100 MB to the cache for every permutation of these five slider values – even if the sliders do not influence the returned data! These additions can make your cache explode very quickly. Please be aware of this limitation if you use widgets in cached functions. We recommend using this feature only for isolated parts of your UI where the widgets directly influence the cached return value.
89 |
90 |
91 |
92 | Support for widgets in cached functions is currently experimental. We may change or remove it anytime without warning. Please use it with care!
93 |
94 |
95 |
96 |
97 | Two widgets are currently not supported in cached functions: st.file_uploader
and st.camera_input
. We may support them in the future. Feel free to open a GitHub issue if you need them!
98 |
99 |
1 | --- 2 | title: st.context 3 | slug: /develop/api-reference/caching-and-state/st.context 4 | description: st.context displays a read-only dict of cookies and headers 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
1 | --- 2 | title: st.experimental_get_query_params 3 | slug: /develop/api-reference/caching-and-state/st.experimental_get_query_params 4 | description: st.experimental_get_query_params returns query parameters currently showing in the browser's URL bar. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.experimental_set_query_params 3 | slug: /develop/api-reference/caching-and-state/st.experimental_set_query_params 4 | description: st.experimental_set_query_params sets query parameters shown in the browser's URL bar. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.query_params
3 | slug: /develop/api-reference/caching-and-state/st.query_params
4 | description: st.query_params reads and manipulates query parameters in the browser's URL bar.
5 | ---
6 |
7 | ## st.query_params
8 |
9 | st.query_params
provides a dictionary-like interface to access query parameters in your app's URL and is available as of Streamlit 1.30.0. It behaves similarly to st.session_state
with the notable exception that keys may be repeated in an app's URL. Handling of repeated keys requires special consideration as explained below.
10 |
11 | st.query_params
can be used with both key and attribute notation. For example, st.query_params.my_key
and st.query_params["my_key"]
. All keys and values will be set and returned as strings. When you write to st.query_params
, key-value pair prefixed with ?
is added to the end of your app's URL. Each additional pair is prefixed with &
instead of ?
. Query parameters are cleared when navigating between pages in a multipage app.
12 |
13 | For example, consider the following URL:
14 |
15 | javascript 16 | https://your_app.streamlit.app/?first_key=1&second_key=two&third_key=true 17 |
18 |
19 | The parameters in the URL above will be accessible in st.query_params
as:
20 |
21 | python 22 | { 23 | "first_key" : "1", 24 | "second_key" : "two", 25 | "third_key" : "true" 26 | } 27 |
28 |
29 | This means you can use those parameters in your app like this:
30 |
31 | python 32 | # You can read query params using key notation 33 | if st.query_params["first_key"] == "1": 34 | do_something() 35 | 36 | # ...or using attribute notation 37 | if st.query_params.second_key == "two": 38 | do_something_else() 39 | 40 | # And you can change a param by just writing to it 41 | st.query_params.first_key = 2 # This gets converted to str automatically 42 |
43 |
44 | ### Repeated keys
45 |
46 | When a key is repeated in your app's URL (?a=1&a=2&a=3
), dict-like methods will return only the last value. In this example, st.query_params["a"]
returns "3"
. To get all keys as a list, use the .get_all()
method shown below. To set the value of a repeated key, assign the values as a list. For example, st.query_params.a = ["1", "2", "3"]
produces the repeated key given at the beginning of this paragraph.
47 |
48 | ### Limitation
49 |
50 | st.query_params
can't get or set embedding settings as described in Embed your app. st.query_params.embed
and st.query_params.embed_options
will raise an AttributeError
or StreamlitAPIException
when trying to get or set their values, respectively.
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
1 | ---
2 | title: Session State
3 | slug: /develop/api-reference/caching-and-state/st.session_state
4 | description: st.session_state is a way to share variables between reruns, for each user session.
5 | ---
6 |
7 | # Session State
8 |
9 | Session State is a way to share variables between reruns, for each user session. In addition to the ability to store and persist state, Streamlit also exposes the ability to manipulate state using Callbacks. Session state also persists across apps inside a multipage app.
10 |
11 | Check out this Session State basics tutorial video by Streamlit Developer Advocate Dr. Marisa Smith to get started:
12 |
13 |
14 |
15 | ### Initialize values in Session State
16 |
17 | The Session State API follows a field-based API, which is very similar to Python dictionaries:
18 |
19 | python 20 | # Initialization 21 | if 'key' not in st.session_state: 22 | st.session_state['key'] = 'value' 23 | 24 | # Session State also supports attribute based syntax 25 | if 'key' not in st.session_state: 26 | st.session_state.key = 'value' 27 |
28 |
29 | ### Reads and updates
30 |
31 | Read the value of an item in Session State and display it by passing to st.write
:
32 |
33 | python 34 | # Read 35 | st.write(st.session_state.key) 36 | 37 | # Outputs: value 38 |
39 |
40 | Update an item in Session State by assigning it a value:
41 |
42 | python 43 | st.session_state.key = 'value2' # Attribute API 44 | st.session_state['key'] = 'value2' # Dictionary like API 45 |
46 |
47 | Curious about what is in Session State? Use st.write
or magic:
48 |
49 | python 50 | st.write(st.session_state) 51 | 52 | # With magic: 53 | st.session_state 54 |
55 |
56 | Streamlit throws a handy exception if an uninitialized variable is accessed:
57 |
58 | python 59 | st.write(st.session_state['value']) 60 | 61 | # Throws an exception! 62 |
63 |
64 |
65 |
66 | ### Delete items
67 |
68 | Delete items in Session State using the syntax to delete items in any Python dictionary:
69 |
70 |
python 71 | # Delete a single key-value pair 72 | del st.session_state[key] 73 | 74 | # Delete all the items in Session state 75 | for key in st.session_state.keys(): 76 | del st.session_state[key] 77 |
78 |
79 | Session State can also be cleared by going to Settings → Clear Cache, followed by Rerunning the app.
80 |
81 |
82 |
83 | ### Session State and Widget State association
84 |
85 | Every widget with a key is automatically added to Session State:
86 |
87 |
python 88 | st.text_input("Your name", key="name") 89 | 90 | # This exists now: 91 | st.session_state.name 92 |
93 |
94 | ### Use Callbacks to update Session State
95 |
96 | A callback is a python function which gets called when an input widget changes.
97 |
98 | Order of execution: When updating Session state in response to events, a callback function gets executed first, and then the app is executed from top to bottom.
99 |
100 | Callbacks can be used with widgets using the parameters on_change
(or on_click
), args
, and kwargs
:
101 |
102 | Parameters
103 |
104 | - on_change or on_click - The function name to be used as a callback
105 | - args (tuple) - List of arguments to be passed to the callback function
106 | - kwargs (dict) - Named arguments to be passed to the callback function
107 |
108 | Widgets which support the on_change
event:
109 |
110 | - st.checkbox
111 | - st.color_picker
112 | - st.date_input
113 | - st.data_editor
114 | - st.file_uploader
115 | - st.multiselect
116 | - st.number_input
117 | - st.radio
118 | - st.select_slider
119 | - st.selectbox
120 | - st.slider
121 | - st.text_area
122 | - st.text_input
123 | - st.time_input
124 | - st.toggle
125 |
126 | Widgets which support the on_click
event:
127 |
128 | - st.button
129 | - st.download_button
130 | - st.form_submit_button
131 |
132 | To add a callback, define a callback function above the widget declaration and pass it to the widget via the on_change
(or on_click
) parameter.
133 |
134 | ### Forms and Callbacks
135 |
136 | Widgets inside a form can have their values be accessed and set via the Session State API. st.form_submit_button
can have a callback associated with it. The callback gets executed upon clicking on the submit button. For example:
137 |
138 | python 139 | def form_callback(): 140 | st.write(st.session_state.my_slider) 141 | st.write(st.session_state.my_checkbox) 142 | 143 | with st.form(key='my_form'): 144 | slider_input = st.slider('My slider', 0, 10, 5, key='my_slider') 145 | checkbox_input = st.checkbox('Yes or No', key='my_checkbox') 146 | submit_button = st.form_submit_button(label='Submit', on_click=form_callback) 147 |
148 |
149 | ### Serializable Session State
150 |
151 | Serialization refers to the process of converting an object or data structure into a format that can be persisted and shared, and allowing you to recover the data’s original structure. Python’s built-in pickle module serializes Python objects to a byte stream ("pickling") and deserializes the stream into an object ("unpickling").
152 |
153 | By default, Streamlit’s Session State allows you to persist any Python object for the duration of the session, irrespective of the object’s pickle-serializability. This property lets you store Python primitives such as integers, floating-point numbers, complex numbers and booleans, dataframes, and even lambdas returned by functions. However, some execution environments may require serializing all data in Session State, so it may be useful to detect incompatibility during development, or when the execution environment will stop supporting it in the future.
154 |
155 | To that end, Streamlit provides a runner.enforceSerializableSessionState
configuration option that, when set to true
, only allows pickle-serializable objects in Session State. To enable the option, either create a global or project config file with the following or use it as a command-line flag:
156 |
157 | toml 158 | # .streamlit/config.toml 159 | [runner] 160 | enforceSerializableSessionState = true 161 |
162 |
163 | By "pickle-serializable", we mean calling pickle.dumps(obj)
should not raise a PicklingError
exception. When the config option is enabled, adding unserializable data to session state should result in an exception. E.g.,
164 |
165 | python 166 | import streamlit as st 167 | 168 | def unserializable_data(): 169 | return lambda x: x 170 | 171 | #👇 results in an exception when enforceSerializableSessionState is on 172 | st.session_state.unserializable = unserializable_data() 173 |
174 |
175 |
176 |
177 |
178 |
179 | When
runner.enforceSerializableSessionState
is set to true
, Session State implicitly uses the pickle
module, which is known to be insecure. Ensure all data saved and retrieved from Session State is trusted because it is possible to construct malicious pickle data that will execute arbitrary code during unpickling. Never load data that could have come from an untrusted source in an unsafe mode or that could have been tampered with. Only load data you trust.
180 |
181 |
182 |
183 | ### Caveats and limitations
184 |
185 | - Only the st.form_submit_button
has a callback in forms. Other widgets inside a form are not allowed to have callbacks.
186 | - on_change
and on_click
events are only supported on input type widgets.
187 | - Modifying the value of a widget via the Session state API, after instantiating it, is not allowed and will raise a StreamlitAPIException
. For example:
188 |
189 | python 190 | slider = st.slider( 191 | label='My Slider', min_value=1, 192 | max_value=10, value=5, key='my_slider') 193 | 194 | st.session_state.my_slider = 7 195 | 196 | # Throws an exception! 197 |
198 |
199 |
200 |
201 | - Setting the widget state via the Session State API and using the
value
parameter in the widget declaration is not recommended, and will throw a warning on the first run. For example:
202 |
203 | python 204 | st.session_state.my_slider = 7 205 | 206 | slider = st.slider( 207 | label='Choose a Value', min_value=1, 208 | max_value=10, value=5, key='my_slider') 209 |
210 |
211 |
212 |
213 | - Setting the state of button-like widgets:
st.button
, st.download_button
, and st.file_uploader
via the Session State API is not allowed. Such type of widgets are by default False and have ephemeral True states which are only valid for a single run. For example:
214 |
215 | python 216 | if 'my_button' not in st.session_state: 217 | st.session_state.my_button = True 218 | 219 | st.button('My button', key='my_button') 220 | 221 | # Throws an exception! 222 |
223 |
224 |
225 |
1 | ---
2 | title: Chart elements
3 | slug: /develop/api-reference/charts
4 | ---
5 |
6 | # Chart elements
7 |
8 | Streamlit supports several different charting libraries, and our goal is to
9 | continually add support for more. Right now, the most basic library in our
10 | arsenal is Matplotlib. Then there are also
11 | interactive charting libraries like Vega
12 | Lite (2D charts) and
13 | deck.gl (maps and 3D charts). And
14 | finally we also provide a few chart types that are "native" to Streamlit,
15 | like st.line_chart
and st.area_chart
.
16 |
17 | ## Simple chart elements
18 |
19 |
20 |
21 |
22 |
23 |
python 28 | st.area_chart(my_data_frame) 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Display a bar chart.
38 |
39 | python 40 | st.bar_chart(my_data_frame) 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | Display a line chart.
50 |
51 | python 52 | st.line_chart(my_data_frame) 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Display a line chart.
62 |
63 | python 64 | st.scatter_chart(my_data_frame) 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Display a map with points on it.
74 |
75 | python 76 | st.map(my_data_frame) 77 |
78 |
79 |
80 |
81 |
82 | ## Advanced chart elements
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | Display a matplotlib.pyplot figure.
91 |
92 | python 93 | st.pyplot(my_mpl_figure) 94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | Display a chart using the Altair library.
103 |
104 | python 105 | st.altair_chart(my_altair_chart) 106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | Display a chart using the Vega-Lite library.
115 |
116 | python 117 | st.vega_lite_chart(my_vega_lite_chart) 118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | Display an interactive Plotly chart.
127 |
128 | python 129 | st.plotly_chart(my_plotly_chart) 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | Display an interactive Bokeh chart.
139 |
140 | python 141 | st.bokeh_chart(my_bokeh_chart) 142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | Display a chart using the PyDeck library.
151 |
152 | python 153 | st.pydeck_chart(my_pydeck_chart) 154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Display a graph using the dagre-d3 library.
163 |
164 | python 165 | st.graphviz_chart(my_graphviz_spec) 166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 | A deceptively simple plotting library for Streamlit. Created by @tvst.
180 |
181 | python 182 | import plost 183 | plost.line_chart(my_dataframe, x='time', y='stock_value', color='stock_name',) 184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | High dimensional Interactive Plotting. Created by @facebookresearch.
195 |
196 | python 197 | data = [{'dropout':0.1, 'lr': 0.001, 'loss': 10.0, 'optimizer': 'SGD'}, {'dropout':0.15, 'lr': 0.01, 'loss': 3.5, 'optimizer': 'Adam'}, {'dropout':0.3, 'lr': 0.1, 'loss': 4.5, 'optimizer': 'Adam'}] 198 | hip.Experiment.from_iterable(data).display() 199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | High dimensional Interactive Plotting. Created by @andfanilo.
210 |
211 | python 212 | from streamlit_echarts import st_echarts 213 | st_echarts(options=options) 214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | Streamlit Component for rendering Folium maps. Created by @randyzwitch.
225 |
226 | python 227 | m = folium.Map(location=[39.949610, -75.150282], zoom_start=16) 228 | st_data = st_folium(m, width=725) 229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | spaCy building blocks and visualizers for Streamlit apps. Created by @explosion.
240 |
241 | python 242 | models = ["en_core_web_sm", "en_core_web_md"] 243 | spacy_streamlit.visualize(models, "Sundar Pichai is the CEO of Google.") 244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 | A Streamlit Graph Vis, based on react-grah-vis. Created by @ChrisDelClea.
255 |
256 | python 257 | from streamlit_agraph import agraph, Node, Edge, Config 258 | agraph(nodes=nodes, edges=edges, config=config) 259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 | Integrate Lottie animations inside your Streamlit app. Created by @andfanilo.
270 |
271 | python 272 | lottie_hello = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_V9t630.json") 273 | st_lottie(lottie_hello, key="hello") 274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 | Make Plotly charts interactive!. Created by @null-jones.
285 |
286 | python 287 | fig = px.line(x=[1], y=[1]) 288 | selected_points = plotly_events(fig) 289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 | A library with useful Streamlit extras. Created by @arnaudmiribel.
300 |
301 | python 302 | chart += get_annotations_chart(annotations=[("Mar 01, 2008", "Pretty good day for GOOG"), ("Dec 01, 2007", "Something's going wrong for GOOG & AAPL"), ("Nov 01, 2008", "Market starts again thanks to..."), ("Dec 01, 2009", "Small crash for GOOG after..."),],) 303 | st.altair_chart(chart, use_container_width=True) 304 |
305 |
306 |
307 |
308 |
309 |
1 | ---
2 | title: st.altair_chart
3 | slug: /develop/api-reference/charts/st.altair_chart
4 | description: st.altair_chart displays a chart using the Altair library.
5 | ---
6 |
7 |
8 |
9 | ## Chart selections
10 |
11 |
12 |
13 |
14 |
15 | ## Theming
16 |
17 | Altair charts are displayed using the Streamlit theme by default. This theme is sleek, user-friendly, and incorporates Streamlit's color palette. The added benefit is that your charts better integrate with the rest of your app's design.
18 |
19 | The Streamlit theme is available from Streamlit 1.16.0 through the theme="streamlit"
keyword argument. To disable it, and use Altair's native theme, use theme=None
instead.
20 |
21 | Let's look at an example of charts with the Streamlit theme and the native Altair theme:
22 |
23 | python 24 | import altair as alt 25 | from vega_datasets import data 26 | 27 | source = data.cars() 28 | 29 | chart = alt.Chart(source).mark_circle().encode( 30 | x='Horsepower', 31 | y='Miles_per_Gallon', 32 | color='Origin', 33 | ).interactive() 34 | 35 | tab1, tab2 = st.tabs(["Streamlit theme (default)", "Altair native theme"]) 36 | 37 | with tab1: 38 | # Use the Streamlit theme. 39 | # This is the default. So you can also omit the theme argument. 40 | st.altair_chart(chart, theme="streamlit", use_container_width=True) 41 | with tab2: 42 | # Use the native Altair theme. 43 | st.altair_chart(chart, theme=None, use_container_width=True) 44 |
45 |
46 | Click the tabs in the interactive app below to see the charts with the Streamlit theme enabled and disabled.
47 |
48 |
49 |
50 | If you're wondering if your own customizations will still be taken into account, don't worry! You can still make changes to your chart configurations. In other words, although we now enable the Streamlit theme by default, you can overwrite it with custom colors or fonts. For example, if you want a chart line to be green instead of the default red, you can do it!
51 |
52 | Here's an example of an Altair chart where manual color passing is done and reflected:
53 |
54 |
55 |
56 | python 57 | import altair as alt 58 | import streamlit as st 59 | from vega_datasets import data 60 | 61 | source = data.seattle_weather() 62 | 63 | scale = alt.Scale( 64 | domain=["sun", "fog", "drizzle", "rain", "snow"], 65 | range=["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"], 66 | ) 67 | color = alt.Color("weather:N", scale=scale) 68 | 69 | # We create two selections: 70 | # - a brush that is active on the top panel 71 | # - a multi-click that is active on the bottom panel 72 | brush = alt.selection_interval(encodings=["x"]) 73 | click = alt.selection_multi(encodings=["color"]) 74 | 75 | # Top panel is scatter plot of temperature vs time 76 | points = ( 77 | alt.Chart() 78 | .mark_point() 79 | .encode( 80 | alt.X("monthdate(date):T", title="Date"), 81 | alt.Y( 82 | "temp_max:Q", 83 | title="Maximum Daily Temperature (C)", 84 | scale=alt.Scale(domain=[-5, 40]), 85 | ), 86 | color=alt.condition(brush, color, alt.value("lightgray")), 87 | size=alt.Size("precipitation:Q", scale=alt.Scale(range=[5, 200])), 88 | ) 89 | .properties(width=550, height=300) 90 | .add_selection(brush) 91 | .transform_filter(click) 92 | ) 93 | 94 | # Bottom panel is a bar chart of weather type 95 | bars = ( 96 | alt.Chart() 97 | .mark_bar() 98 | .encode( 99 | x="count()", 100 | y="weather:N", 101 | color=alt.condition(click, color, alt.value("lightgray")), 102 | ) 103 | .transform_filter(brush) 104 | .properties( 105 | width=550, 106 | ) 107 | .add_selection(click) 108 | ) 109 | 110 | chart = alt.vconcat(points, bars, data=source, title="Seattle Weather: 2012-2015") 111 | 112 | tab1, tab2 = st.tabs(["Streamlit theme (default)", "Altair native theme"]) 113 | 114 | with tab1: 115 | st.altair_chart(chart, theme="streamlit", use_container_width=True) 116 | with tab2: 117 | st.altair_chart(chart, theme=None, use_container_width=True) 118 |
119 |
120 |
121 |
122 | Notice how the custom colors are still reflected in the chart, even when the Streamlit theme is enabled 👇
123 |
124 |
125 |
126 | For many more examples of Altair charts with and without the Streamlit theme, check out the altair.streamlit.app.
127 |
1 | --- 2 | title: st.area_chart 3 | slug: /develop/api-reference/charts/st.area_chart 4 | description: st.area_chart displays an area chart. 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | --- 2 | title: st.bar_chart 3 | slug: /develop/api-reference/charts/st.bar_chart 4 | description: st.bar_chart displays a bar chart. 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | --- 2 | title: st.bokeh_chart 3 | slug: /develop/api-reference/charts/st.bokeh_chart 4 | description: st.bokeh_chart displays an interactive Bokeh chart. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.graphviz_chart 3 | slug: /develop/api-reference/charts/st.graphviz_chart 4 | description: st.graphviz_chart displays a graph using the dagre-d3 library. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.line_chart 3 | slug: /develop/api-reference/charts/st.line_chart 4 | description: st.line_chart displays a line chart. 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | --- 2 | title: st.map 3 | slug: /develop/api-reference/charts/st.map 4 | description: st.map displays a map with points on it. 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | ---
2 | title: st.plotly_chart
3 | slug: /develop/api-reference/charts/st.plotly_chart
4 | description: st.plotly_chart displays an interactive Plotly chart.
5 | ---
6 |
7 |
8 |
9 | ## Chart selections
10 |
11 |
12 |
13 |
14 |
15 | ## Theming
16 |
17 | Plotly charts are displayed using the Streamlit theme by default. This theme is sleek, user-friendly, and incorporates Streamlit's color palette. The added benefit is that your charts better integrate with the rest of your app's design.
18 |
19 | The Streamlit theme is available from Streamlit 1.16.0 through the theme="streamlit"
keyword argument. To disable it, and use Plotly's native theme, use theme=None
instead.
20 |
21 | Let's look at an example of charts with the Streamlit theme and the native Plotly theme:
22 |
23 | python 24 | import plotly.express as px 25 | import streamlit as st 26 | 27 | df = px.data.gapminder() 28 | 29 | fig = px.scatter( 30 | df.query("year==2007"), 31 | x="gdpPercap", 32 | y="lifeExp", 33 | size="pop", 34 | color="continent", 35 | hover_name="country", 36 | log_x=True, 37 | size_max=60, 38 | ) 39 | 40 | tab1, tab2 = st.tabs(["Streamlit theme (default)", "Plotly native theme"]) 41 | with tab1: 42 | # Use the Streamlit theme. 43 | # This is the default. So you can also omit the theme argument. 44 | st.plotly_chart(fig, theme="streamlit", use_container_width=True) 45 | with tab2: 46 | # Use the native Plotly theme. 47 | st.plotly_chart(fig, theme=None, use_container_width=True) 48 |
49 |
50 | Click the tabs in the interactive app below to see the charts with the Streamlit theme enabled and disabled.
51 |
52 |
53 |
54 | If you're wondering if your own customizations will still be taken into account, don't worry! You can still make changes to your chart configurations. In other words, although we now enable the Streamlit theme by default, you can overwrite it with custom colors or fonts. For example, if you want a chart line to be green instead of the default red, you can do it!
55 |
56 | Here's an example of an Plotly chart where a custom color scale is defined and reflected:
57 |
58 | python 59 | import plotly.express as px 60 | import streamlit as st 61 | 62 | st.subheader("Define a custom colorscale") 63 | df = px.data.iris() 64 | fig = px.scatter( 65 | df, 66 | x="sepal_width", 67 | y="sepal_length", 68 | color="sepal_length", 69 | color_continuous_scale="reds", 70 | ) 71 | 72 | tab1, tab2 = st.tabs(["Streamlit theme (default)", "Plotly native theme"]) 73 | with tab1: 74 | st.plotly_chart(fig, theme="streamlit", use_container_width=True) 75 | with tab2: 76 | st.plotly_chart(fig, theme=None, use_container_width=True) 77 |
78 |
79 | Notice how the custom color scale is still reflected in the chart, even when the Streamlit theme is enabled 👇
80 |
81 |
82 |
83 | For many more examples of Plotly charts with and without the Streamlit theme, check out the plotly.streamlit.app.
84 |
1 | --- 2 | title: st.pydeck_chart 3 | slug: /develop/api-reference/charts/st.pydeck_chart 4 | description: st.pydeck_chart displays a chart using the PyDeck library. 5 | --- 6 | 7 | 8 | 9 | ## Chart selections 10 | 11 | 12 | 13 | 14 |
1 | ---
2 | title: st.pyplot
3 | slug: /develop/api-reference/charts/st.pyplot
4 | description: st.pyplot displays a matplotlib.pyplot figure.
5 | ---
6 |
7 |
8 |
9 |
10 | Matplotlib doesn't work well with threads. So if you're using Matplotlib you should wrap your code with locks. This Matplotlib bug is more prominent when you deploy and share your apps because you're more likely to get concurrent users then. The following example uses Rlock
from the threading
module.
11 |
12 | python 13 | import streamlit as st 14 | import matplotlib.pyplot as plt 15 | import numpy as np 16 | from threading import RLock 17 | 18 | _lock = RLock() 19 | 20 | x = np.random.normal(1, 1, 100) 21 | y = np.random.normal(1, 1, 100) 22 | 23 | with _lock: 24 | fig, ax = plt.subplots() 25 | ax.scatter(x, y) 26 | st.pyplot(fig) 27 |
28 |
29 |
30 |
1 | --- 2 | title: st.scatter_chart 3 | slug: /develop/api-reference/charts/st.scatter_chart 4 | description: st.scatter_chart displays an scatter chart. 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | ---
2 | title: st.vega_lite_chart
3 | slug: /develop/api-reference/charts/st.vega_lite_chart
4 | description: st.vega_lite_chart displays a chart using the Vega-Lite library.
5 | ---
6 |
7 |
8 |
9 | ## Chart selections
10 |
11 |
12 |
13 |
14 |
15 | ## Theming
16 |
17 | Vega-Lite charts are displayed using the Streamlit theme by default. This theme is sleek, user-friendly, and incorporates Streamlit's color palette. The added benefit is that your charts better integrate with the rest of your app's design.
18 |
19 | The Streamlit theme is available from Streamlit 1.16.0 through the theme="streamlit"
keyword argument. To disable it, and use Vega-Lite's native theme, use theme=None
instead.
20 |
21 | Let's look at an example of charts with the Streamlit theme and the native Vega-Lite theme:
22 |
23 | python 24 | import streamlit as st 25 | from vega_datasets import data 26 | 27 | source = data.cars() 28 | 29 | chart = { 30 | "mark": "point", 31 | "encoding": { 32 | "x": { 33 | "field": "Horsepower", 34 | "type": "quantitative", 35 | }, 36 | "y": { 37 | "field": "Miles_per_Gallon", 38 | "type": "quantitative", 39 | }, 40 | "color": {"field": "Origin", "type": "nominal"}, 41 | "shape": {"field": "Origin", "type": "nominal"}, 42 | }, 43 | } 44 | 45 | tab1, tab2 = st.tabs(["Streamlit theme (default)", "Vega-Lite native theme"]) 46 | 47 | with tab1: 48 | # Use the Streamlit theme. 49 | # This is the default. So you can also omit the theme argument. 50 | st.vega_lite_chart( 51 | source, chart, theme="streamlit", use_container_width=True 52 | ) 53 | with tab2: 54 | st.vega_lite_chart( 55 | source, chart, theme=None, use_container_width=True 56 | ) 57 |
58 |
59 | Click the tabs in the interactive app below to see the charts with the Streamlit theme enabled and disabled.
60 |
61 |
62 |
63 | If you're wondering if your own customizations will still be taken into account, don't worry! You can still make changes to your chart configurations. In other words, although we now enable the Streamlit theme by default, you can overwrite it with custom colors or fonts. For example, if you want a chart line to be green instead of the default red, you can do it!
64 |
1 | ---
2 | title: Chat elements
3 | slug: /develop/api-reference/chat
4 | ---
5 |
6 | # Chat elements
7 |
8 | Streamlit provides a few commands to help you build conversational apps. These chat elements are designed to be used in conjunction with each other, but you can also use them separately.
9 |
10 | st.chat_message
lets you insert a chat message container into the app so you can display messages from the user or the app. Chat containers can contain other Streamlit elements, including charts, tables, text, and more. st.chat_input
lets you display a chat input widget so the user can type in a message. Remember to check out st.status
to display output from long-running processes and external API calls.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
python 22 | prompt = st.chat_input("Say something") 23 | if prompt: 24 | st.write(f"The user has sent: {prompt}") 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Insert a chat message container.
35 |
36 | python 37 | import numpy as np 38 | with st.chat_message("user"): 39 | st.write("Hello 👋") 40 | st.line_chart(np.random.randn(30, 3)) 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Display output of long-running tasks in a container.
51 |
52 | python 53 | with st.status('Running'): 54 | do_something_slow() 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Write generators or streams to the app with a typewriter effect.
63 |
64 | python 65 | st.write_stream(my_generator) 66 | st.write_stream(my_llm_stream) 67 |
68 |
69 |
70 |
71 |
1 | ---
2 | title: st.chat_input
3 | slug: /develop/api-reference/chat/st.chat_input
4 | description: st.chat_input displays a chat input widget.
5 | ---
6 |
7 |
8 |
9 | Read the Build a basic LLM chat app tutorial to learn how to use st.chat_message
and st.chat_input
to build chat-based apps.
10 |
11 |
12 |
13 |
14 |
15 | For an overview of the st.chat_input
and st.chat_message
API, check out this video tutorial by Chanin Nantasenamat (@dataprofessor), a Senior Developer Advocate at Streamlit.
16 |
17 |
18 |
1 | ---
2 | title: st.chat_message
3 | slug: /develop/api-reference/chat/st.chat_message
4 | description: st.chat_message inserts a chat message container into the app.
5 | ---
6 |
7 |
8 |
9 | Read the Build a basic LLM chat app tutorial to learn how to use st.chat_message
and st.chat_input
to build chat-based apps.
10 |
11 |
12 |
13 |
14 |
15 | For an overview of the st.chat_message
and st.chat_input
API, check out this video tutorial by Chanin Nantasenamat (@dataprofessor), a Senior Developer Advocate at Streamlit.
16 |
17 |
18 |
1 | ---
2 | title: Command-line options
3 | slug: /develop/api-reference/cli
4 | ---
5 |
6 | # Command-line interface
7 |
8 | When you install Streamlit, a command-line (CLI) tool gets installed
9 | as well. The purpose of this tool is to run Streamlit apps, change Streamlit configuration options,
10 | and help you diagnose and fix issues.
11 |
12 | ## Available commands
13 |
14 | - streamlit cache clear
: Clear the on-disk cache.
15 | - streamlit config show
: Show all configuration options.
16 | - streamlit docs
: Open the Streamlit docs.
17 | - streamlit hello
: Run an example Streamlit app.
18 | - streamlit help
: Show the available CLI commands.
19 | - streamlit init
: Create the files for a new Streamlit app.
20 | - streamlit run
: Run your Streamlit app.
21 | - streamlit version
: Show the version of Streamlit.
22 |
23 | ### Run your app
24 |
25 | The most important command is streamlit run
, which is summarized for convenience here:
26 |
27 | bash 28 | streamlit run your_script.py 29 |
30 |
31 | At any time, in your terminal, you can stop the server with Ctrl+C.
32 |
1 | ---
2 | title: streamlit cache
3 | slug: /develop/api-reference/cli/cache
4 | ---
5 |
6 | ## $ streamlit cache clear
7 |
8 | Clear persisted files from the on-disk Streamlit cache, if present.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit cache clear 14 |
15 |
1 | ---
2 | title: streamlit config show
3 | slug: /develop/api-reference/cli/config
4 | ---
5 |
6 | ## $ streamlit config show
7 |
8 | Print all the available configuration options, including their descriptions, default values, and current values. For more information about configuration options, see config.toml
.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit config show 14 |
15 |
1 | ---
2 | title: streamlit docs
3 | slug: /develop/api-reference/cli/docs
4 | ---
5 |
6 | ## $ streamlit docs
7 |
8 | Open the Streamlit docs in your default browser.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit docs 14 |
15 |
1 | ---
2 | title: streamlit hello
3 | slug: /develop/api-reference/cli/hello
4 | ---
5 |
6 | ## $ streamlit hello
7 |
8 | Run the Hello app, an example Streamlit app included with the Streamlit library.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit hello 14 |
15 |
16 | ### Options
17 |
18 | The hello
command accepts configuration options (just like the run
command does). Configuration options are passed in the form of --<section>.<option>=<value>
. For example, if you want to set the primary color of your app to blue, you could use one of the three equivalent options:
19 |
20 | - --theme.primaryColor=blue
21 | - --theme.primaryColor="blue"
22 | - --theme.primaryColor=#0000FF
23 |
24 | For a complete list of configuration options, see config.toml
in the API reference. For examples, see below.
25 |
26 | ### Example
27 |
28 | #### Example 1: Run the Hello app with default settings
29 |
30 | To verify that Streamlit is installed correctly, this command runs an example app included in the Streamlit library. From any directory, execute the following:
31 |
32 | 33 | streamlit hello 34 |
35 |
36 | Streamlit will start the Hello app and open it in your default browser. The source for the Hello app can be viewed in GitHub.
37 |
38 | #### Example 2: Run the Hello app with a custom config option value
39 |
40 | To run the Hello app with a blue accent color, from any directory, execute the following:
41 |
42 | 43 | streamlit hello --theme.primaryColor=blue 44 |
45 |
1 | ---
2 | title: streamlit help
3 | slug: /develop/api-reference/cli/help
4 | ---
5 |
6 | ## $ streamlit help
7 |
8 | Print the available commands for the Streamlit CLI tool. This command is equivalent to executing streamlit --help
.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit help 14 |
15 |
1 | ---
2 | title: streamlit init
3 | slug: /develop/api-reference/cli/init
4 | ---
5 |
6 | ## $ streamlit init
7 |
8 | This command creates the files for a new Streamlit app.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit init <directory> 14 |
15 |
16 | ### Arguments
17 |
18 | <directory>
(Optional): The directory location of the new project. If no directory is provided, the current working directory will be used.
19 |
20 | ### Examples
21 |
22 | #### Example 1: Create project files the current working directory
23 |
24 | 1. In your current working directory (CWD), execute the following:
25 |
26 | bash 27 | streamlit init 28 |
29 |
30 | Streamlit creates the following files:
31 | 32 | CWD/ 33 | ├── requirements.txt 34 | └── streamlit_app.py 35 |
36 |
37 | 2. In your terminal, Streamlit prompts, ❓ Run the app now? [Y/n]
. Enter Y
for yes.
38 |
39 | This is equivalent to executing streamlit run streamlit_app.py
from your current working directory.
40 |
41 | 3. Begin editing your streamlit_app.py
file and save your changes.
42 |
43 | #### Example 2: Create project files in another directory
44 |
45 | 1. In your current working directory (CWD), execute the following:
46 |
47 | bash 48 | streamlit init project 49 |
50 |
51 | Streamlit creates the following files:
52 | 53 | CWD/ 54 | └── project/ 55 | ├── requirements.txt 56 | └── streamlit_app.py 57 |
58 |
59 | 2. In your terminal, Streamlit prompts, ❓ Run the app now? [Y/n]
. Enter Y
for yes.
60 |
61 | This is equivalent to executing streamlit run project/streamlit_app.py
from your current working directory.
62 |
63 | 3. Begin editing your streamlit_app.py
file and save your changes.
64 |
1 | ---
2 | title: streamlit run
3 | slug: /develop/api-reference/cli/run
4 | ---
5 |
6 | ## $ streamlit run
7 |
8 | This command starts your Streamlit app.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit run <entrypoint file> [-- config options] [script args] 14 |
15 |
16 | ### Arguments
17 |
18 | <entrypoint file>
: The path to your entrypoint file for your Streamlit app. In a multipage app with st.navigation
, your entrypoint file acts as a router between your pages. Otherwise, your entrypoint file is your app's homepage.
19 |
20 | ### Options
21 |
22 | Configuration options are passed in the form of --<section>.<option>=<value>
. For example, if you want to set the primary color of your app to blue, you could use one of the three equivalent options:
23 |
24 | - --theme.primaryColor=blue
25 | - --theme.primaryColor="blue"
26 | - --theme.primaryColor=#0000FF
27 |
28 | For a complete list of configuration options, see config.toml
in the API reference. For examples, see below.
29 |
30 | ### Script arguments
31 |
32 | If you need to pass arguments directly to your script, you can pass them as positional arguments. If you use sys.argv
to read your arguments, sys.arfgv
returns a list of all arugments and does not include any configuration options. Python interprets all arguments as strings.
33 |
34 | - sys.argv[0]
returns the provided path to your entrypoint file (<entrypoint file>
).
35 | - sys.argv[1:]
returns a list of arguments in order and does not include any configuration options.
36 |
37 | ### Examples
38 |
39 | - If your app is in your working directory, run it as follows:
40 |
41 | 42 | streamlit run your_app.py 43 |
44 |
45 | - If your app is in a subdirectory, run it as follows:
46 |
47 | 48 | streamlit run your_subdirectory/your_app.py 49 |
50 |
51 | - If your app is saved in a public GitHub repo or gist, run it as follows:
52 |
53 | 54 | streamlit run https://raw.githubusercontent.com/streamlit/demo-uber-nyc-pickups/master/streamlit_app.py 55 |
56 |
57 | - If you need to set one or more configuration options, run it as follows:
58 |
59 | 60 | streamlit run your_app.py --client.showErrorDetails=False --theme.primaryColor=blue 61 |
62 |
63 | - If you need to pass an argument to your script, run it as follows:
64 |
65 | 66 | streamlit run your_app.py "my list" of arguments 67 |
68 |
69 | Within your script, the following statement will be true:
70 |
71 | 72 | sys.argv[0] == "your_app.py" 73 | sys.argv[1] == "my list" 74 | sys.argv[2] == "of" 75 | sys.argv[3] == "arguments" 76 |
77 |
1 | ---
2 | title: streamlit version
3 | slug: /develop/api-reference/cli/version
4 | ---
5 |
6 | ## $ streamlit version
7 |
8 | Print Streamlit's version number. This command is equivalent to executing streamlit --version
.
9 |
10 | ### Syntax
11 |
12 | 13 | streamlit version 14 |
15 |
1 | --- 2 | title: Configuration 3 | slug: /develop/api-reference/configuration 4 | --- 5 | 6 | # Configuration 7 | 8 | 9 | 10 | 11 |
12 | 13 | Configures the default settings for your app. 14 | 15 |16 | your-project/ 17 | ├── .streamlit/ 18 | │ └── config.toml 19 | └── your_app.py 20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Retrieve a single configuration option.
28 |
29 | python 30 | st.get_option("theme.primaryColor") 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Set a single configuration option. (This is very limited.)
39 |
40 | python 41 | st.set_option("deprecation.showPyplotGlobalUse", False) 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | Configures the default settings of the page.
50 |
51 | python 52 | st.set_page_config( 53 | page_title="My app", 54 | page_icon=":shark:", 55 | ) 56 |
57 |
58 |
59 |
60 |
1 | ---
2 | title: config.toml
3 | slug: /develop/api-reference/configuration/config.toml
4 | ---
5 |
6 | ## config.toml
7 |
8 | config.toml
is an optional file you can define for your working directory or global development environment. When config.toml
is defined both globally and in your working directory, Streamlit combines the configuration options and gives precedence to the working-directory configuration. Additionally, you can use environment variables and command-line options to override additional configuration options. For more information, see Configuration options.
9 |
10 | ### File location
11 |
12 | To define your configuration locally or per-project, add .streamlit/config.toml
to your working directory. Your working directory is wherever you call streamlit run
. If you haven't previously created the .streamlit
directory, you will need to add it.
13 |
14 | To define your configuration globally, you must first locate your global .streamlit
directory. Streamlit adds this hidden directory to your OS user profile during installation. For MacOS/Linux, this will be ~/.streamlit/config.toml
. For Windows, this will be %userprofile%/.streamlit/config.toml
.
15 |
16 | ### File format
17 |
18 | config.toml
is a TOML file.
19 |
20 | #### Example
21 |
22 | toml 23 | [client] 24 | showErrorDetails = "none" 25 | 26 | [theme] 27 | primaryColor = "#F63366" 28 | backgroundColor = "black" 29 |
30 |
31 | ### Available configuration options
32 |
33 | Below are all the sections and options you can have in your .streamlit/config.toml
file. To see all configurations, use the following command in your terminal or CLI:
34 |
35 | bash 36 | streamlit config show 37 |
38 |
39 | #### Global
40 |
41 | toml 42 | [global] 43 | 44 | # By default, Streamlit displays a warning when a user sets both a widget 45 | # default value in the function defining the widget and a widget value via 46 | # the widget's key in `st.session_state`. 47 | # If you'd like to turn off this warning, set this to True. 48 | # Default: false 49 | disableWidgetStateDuplicationWarning = false 50 | 51 | # If True, will show a warning when you run a Streamlit-enabled script 52 | # via "python my_script.py". 53 | # Default: true 54 | showWarningOnDirectExecution = true 55 |
56 |
57 | #### Logger
58 |
59 | toml 60 | [logger] 61 | 62 | # Level of logging for Streamlit's internal logger: "error", "warning", 63 | # "info", or "debug". 64 | # Default: "info" 65 | level = "info" 66 | 67 | # String format for logging messages. If logger.datetimeFormat is set, 68 | # logger messages will default to `%(asctime)s.%(msecs)03d %(message)s`. 69 | # See Python's documentation for available attributes: 70 | # https://docs.python.org/3/library/logging.html#formatter-objects 71 | # Default: "%(asctime)s %(message)s" 72 | messageFormat = "%(asctime)s %(message)s" 73 |
74 |
75 | #### Client
76 |
77 | toml 78 | [client] 79 | 80 | # Controls whether uncaught app exceptions and deprecation warnings 81 | # are displayed in the browser. This can be one of the following: 82 | # - "full" : In the browser, Streamlit displays app deprecation 83 | # warnings and exceptions, including exception types, 84 | # exception messages, and associated tracebacks. 85 | # - "stacktrace" : In the browser, Streamlit displays exceptions, 86 | # including exception types, generic exception messages, 87 | # and associated tracebacks. Deprecation warnings and 88 | # full exception messages will only print to the 89 | # console. 90 | # - "type" : In the browser, Streamlit displays exception types and 91 | # generic exception messages. Deprecation warnings, full 92 | # exception messages, and associated tracebacks only 93 | # print to the console. 94 | # - "none" : In the browser, Streamlit displays generic exception 95 | # messages. Deprecation warnings, full exception 96 | # messages, associated tracebacks, and exception types 97 | # will only print to the console. 98 | # - True : This is deprecated. Streamlit displays "full" 99 | # error details. 100 | # - False : This is deprecated. Streamlit displays "stacktrace" 101 | # error details. 102 | # Default: "full" 103 | showErrorDetails = "full" 104 | 105 | # Change the visibility of items in the toolbar, options menu, 106 | # and settings dialog (top right of the app). 107 | # Allowed values: 108 | # - "auto" : Show the developer options if the app is accessed through 109 | # localhost or through Streamlit Community Cloud as a developer. 110 | # Hide them otherwise. 111 | # - "developer" : Show the developer options. 112 | # - "viewer" : Hide the developer options. 113 | # - "minimal" : Show only options set externally (e.g. through 114 | # Streamlit Community Cloud) or through st.set_page_config. 115 | # If there are no options left, hide the menu. 116 | # Default: "auto" 117 | toolbarMode = "auto" 118 | 119 | # Controls whether to display the default sidebar page navigation in a 120 | # multi-page app. This only applies when app's pages are defined by the 121 | # `pages/` directory. 122 | # Default: true 123 | showSidebarNavigation = true 124 |
125 |
126 | #### Runner
127 |
128 | toml 129 | [runner] 130 | 131 | # Allows you to type a variable or string by itself in a single line of 132 | # Python code to write it to the app. 133 | # Default: true 134 | magicEnabled = true 135 | 136 | # Handle script rerun requests immediately, rather than waiting for script 137 | # execution to reach a yield point. This makes Streamlit much more 138 | # responsive to user interaction, but it can lead to race conditions in 139 | # apps that mutate session_state data outside of explicit session_state 140 | # assignment statements. 141 | # Default: true 142 | fastReruns = true 143 | 144 | # Raise an exception after adding unserializable data to Session State. 145 | # Some execution environments may require serializing all data in Session 146 | # State, so it may be useful to detect incompatibility during development, 147 | # or when the execution environment will stop supporting it in the future. 148 | # Default: false 149 | enforceSerializableSessionState = false 150 | 151 | # Adjust how certain 'options' widgets like radio, selectbox, and 152 | # multiselect coerce Enum members when the Enum class gets re-defined 153 | # during a script re-run. For more information, check out the docs: 154 | # https://docs.streamlit.io/develop/concepts/design/custom-classes#enums 155 | # Allowed values: 156 | # - "off" : Disables Enum coercion. 157 | # - "nameOnly" : Enum classes can be coerced if their member names match. 158 | # - "nameAndValue" : Enum classes can be coerced if their member names AND 159 | # member values match. 160 | # Default: "nameOnly" 161 | enumCoercion = "nameOnly" 162 |
163 |
164 | #### Server
165 |
166 | toml 167 | [server] 168 | 169 | # List of folders that should not be watched for changes. 170 | # Relative paths will be taken as relative to the current working directory. 171 | # Example: ['/home/user1/env', 'relative/path/to/folder'] 172 | # Default: [] 173 | folderWatchBlacklist = [] 174 | 175 | # Change the type of file watcher used by Streamlit, or turn it off 176 | # completely. 177 | # Allowed values: 178 | # - "auto" : Streamlit will attempt to use the watchdog module, and 179 | # falls back to polling if watchdog is not available. 180 | # - "watchdog" : Force Streamlit to use the watchdog module. 181 | # - "poll" : Force Streamlit to always use polling. 182 | # - "none" : Streamlit will not watch files. 183 | # Default: "auto" 184 | fileWatcherType = "auto" 185 | 186 | # Symmetric key used to produce signed cookies. If deploying on multiple 187 | # replicas, this should be set to the same value across all replicas to ensure 188 | # they all share the same secret. 189 | # Default: randomly generated secret key. 190 | cookieSecret = "a-random-key-appears-here" 191 | 192 | # If false, will attempt to open a browser window on start. 193 | # Default: false unless (1) we are on a Linux box where DISPLAY is unset, or 194 | # (2) we are running in the Streamlit Atom plugin. 195 | headless = false 196 | 197 | # Automatically rerun script when the file is modified on disk. 198 | # Default: false 199 | runOnSave = false 200 | 201 | # The address where the server will listen for client and browser 202 | # connections. Use this if you want to bind the server to a specific address. 203 | # If set, the server will only be accessible from this address, and not from 204 | # any aliases (like localhost). 205 | # Default: (unset) 206 | address = 207 | 208 | # The port where the server will listen for browser connections. 209 | # Don't use port 3000 which is reserved for internal development. 210 | # Default: 8501 211 | port = 8501 212 | 213 | # The base path for the URL where Streamlit should be served from. 214 | # Default: "" 215 | baseUrlPath = "" 216 | 217 | # Enables support for Cross-Origin Resource Sharing (CORS) protection, 218 | # for added security. 219 | # If XSRF protection is enabled and CORS protection is disabled at the 220 | # same time, Streamlit will enable them both instead. 221 | # Default: true 222 | enableCORS = true 223 | 224 | # Enables support for Cross-Site Request Forgery (XSRF) protection, for 225 | # added security. 226 | # If XSRF protection is enabled and CORS protection is disabled at the 227 | # same time, Streamlit will enable them both instead. 228 | # Default: true 229 | enableXsrfProtection = true 230 | 231 | # Max size, in megabytes, for files uploaded with the file_uploader. 232 | # Default: 200 233 | maxUploadSize = 200 234 | 235 | # Max size, in megabytes, of messages that can be sent via the WebSocket 236 | # connection. 237 | # Default: 200 238 | maxMessageSize = 200 239 | 240 | # Enables support for websocket compression. 241 | # Default: false 242 | enableWebsocketCompression = false 243 | 244 | # Enable serving files from a `static` directory in the running app's 245 | # directory. 246 | # Default: false 247 | enableStaticServing = false 248 | 249 | # TTL in seconds for sessions whose websockets have been disconnected. The server 250 | # may choose to clean up session state, uploaded files, etc for a given session 251 | # with no active websocket connection at any point after this time has passed. 252 | # Default: 120 253 | disconnectedSessionTTL = 120 254 | 255 | # Server certificate file for connecting via HTTPS. 256 | # Must be set at the same time as "server.sslKeyFile". 257 | # ['DO NOT USE THIS OPTION IN A PRODUCTION ENVIRONMENT. It has not gone through 258 | # security audits or performance tests. For the production environment, we 259 | # recommend performing SSL termination by the load balancer or the reverse 260 | # proxy.'] 261 | sslCertFile = 262 | 263 | # Cryptographic key file for connecting via HTTPS. 264 | # Must be set at the same time as "server.sslCertFile". 265 | # ['DO NOT USE THIS OPTION IN A PRODUCTION ENVIRONMENT. It has not gone through 266 | # security audits or performance tests. For the production environment, we 267 | # recommend performing SSL termination by the load balancer or the reverse 268 | # proxy.'] 269 | sslKeyFile = 270 |
271 |
272 | #### Browser
273 |
274 | toml 275 | [browser] 276 | 277 | # Internet address where users should point their browsers in order to 278 | # connect to the app. Can be IP address or DNS name and path. 279 | # This is used to: 280 | # - Set the correct URL for CORS and XSRF protection purposes. 281 | # - Show the URL on the terminal 282 | # - Open the browser 283 | # Default: "localhost" 284 | serverAddress = "localhost" 285 | 286 | # Whether to send usage statistics to Streamlit. 287 | # Default: true 288 | gatherUsageStats = true 289 | 290 | # Port where users should point their browsers in order to connect to the 291 | # app. 292 | # This is used to: 293 | # - Set the correct URL for XSRF protection purposes. 294 | # - Show the URL on the terminal (part of `streamlit run`). 295 | # - Open the browser automatically (part of `streamlit run`). 296 | # This option is for advanced use cases. To change the port of your app, use 297 | # `server.Port` instead. Don't use port 3000 which is reserved for internal 298 | # development. 299 | # Default: whatever value is set in server.port. 300 | serverPort = 8501 301 |
302 |
303 | #### Mapbox
304 |
305 | toml 306 | [mapbox] 307 | 308 | # Configure Streamlit to use a custom Mapbox 309 | # token for elements like st.pydeck_chart and st.map. 310 | # To get a token for yourself, create an account at 311 | # https://mapbox.com. It's free (for moderate usage levels)! 312 | # Default: "" 313 | token = "" 314 |
315 |
316 | #### Theme
317 |
318 | toml 319 | [theme] 320 | 321 | # The preset Streamlit theme that your custom theme inherits from. 322 | # This can be one of the following: "light" or "dark". 323 | base = 324 | 325 | # Primary accent color. 326 | primaryColor = 327 | 328 | # Background color of the app. 329 | backgroundColor = 330 | 331 | # Background color used for most interactive widgets. 332 | secondaryBackgroundColor = 333 | 334 | # Color used for almost all text. 335 | textColor = 336 | 337 | # Color used for all links. 338 | linkColor = 339 | 340 | # Background color used for code blocks. 341 | codeBackgroundColor = 342 | 343 | # The font family for all text, except code blocks. This can be one of 344 | # the following: 345 | # - "sans-serif" 346 | # - "serif" 347 | # - "monospace" 348 | # - the `font` value for a custom font table under [[theme.fontFaces]] 349 | # - a comma-separated list of these (as a single string) to specify 350 | # fallbacks 351 | # For example, you can use the following: 352 | # font = "cool-font, fallback-cool-font, sans-serif" 353 | font = 354 | 355 | # The font family to use for code (monospace) in the sidebar. This can be 356 | # one of the following: 357 | # - "sans-serif" 358 | # - "serif" 359 | # - "monospace" 360 | # - the `font` value for a custom font table under [[theme.fontFaces]] 361 | # - a comma-separated list of these (as a single string) to specify 362 | # fallbacks 363 | codeFont = 364 | 365 | # The font family to use for headings. This can be one of the following: 366 | # - "sans-serif" 367 | # - "serif" 368 | # - "monospace" 369 | # - the `font` value for a custom font table under [[theme.fontFaces]] 370 | # - a comma-separated list of these (as a single string) to specify 371 | # fallbacks 372 | # If no heading font is set, Streamlit uses `theme.font` for headings. 373 | headingFont = 374 | 375 | # An array of fonts to use in your app. Each font in the array is a table 376 | # (dictionary) with the following three attributes: font, url, weight, 377 | # and style. To host a font with your app, enable static file serving 378 | # with `server.enableStaticServing=true`. You can define multiple 379 | # [[theme.fontFaces]] tables. 380 | # For example, each font is defined in a [[theme.fontFaces]] table as 381 | # follows: 382 | # [[theme.fontFaces]] 383 | # font = "font_name" 384 | # url = "app/static/font_file.woff" 385 | # weight = 400 386 | # style = "normal" 387 | fontFaces = 388 | 389 | # The radius used as basis for the corners of most UI elements. This can 390 | # be one of the following: "none", "small", "medium", "large", "full", 391 | # or the number in pixels or rem. For example, you can use "10px", 392 | # "0.5rem", or "2rem". To follow best practices, use rem instead of 393 | # pixels when specifying a numeric size. 394 | baseRadius = 395 | 396 | # The color of the border around elements. 397 | borderColor = 398 | 399 | # Whether to show a border around input widgets. 400 | showWidgetBorder = 401 | 402 | # Sets the root font size (in pixels) for the app, which determines the 403 | # overall scale of text and UI elements. The default base font size is 16. 404 | baseFontSize = 405 | 406 | # Whether to show a vertical separator between the sidebar and the main 407 | # content area. 408 | showSidebarBorder = 409 |
410 |
411 | #### Sidebar theme
412 |
413 | toml 414 | [theme.sidebar] 415 | 416 | # Primary accent color. 417 | primaryColor = 418 | 419 | # Background color of the app. 420 | backgroundColor = 421 | 422 | # Background color used for most interactive widgets. 423 | secondaryBackgroundColor = 424 | 425 | # Color used for almost all text. 426 | textColor = 427 | 428 | # Color used for all links. 429 | linkColor = 430 | 431 | # Background color used for code blocks. 432 | codeBackgroundColor = 433 | 434 | # The font family for all text, except code blocks. This can be one of 435 | # the following: 436 | # - "sans-serif" 437 | # - "serif" 438 | # - "monospace" 439 | # - the `font` value for a custom font table under [[theme.fontFaces]] 440 | # - a comma-separated list of these (as a single string) to specify 441 | # fallbacks 442 | # For example, you can use the following: 443 | # font = "cool-font, fallback-cool-font, sans-serif" 444 | font = 445 | 446 | # The font family to use for code (monospace) in the sidebar. This can be 447 | # one of the following: 448 | # - "sans-serif" 449 | # - "serif" 450 | # - "monospace" 451 | # - the `font` value for a custom font table under [[theme.fontFaces]] 452 | # - a comma-separated list of these (as a single string) to specify 453 | # fallbacks 454 | codeFont = 455 | 456 | # The font family to use for headings. This can be one of the following: 457 | # - "sans-serif" 458 | # - "serif" 459 | # - "monospace" 460 | # - the `font` value for a custom font table under [[theme.fontFaces]] 461 | # - a comma-separated list of these (as a single string) to specify 462 | # fallbacks 463 | # If no heading font is set, Streamlit uses `theme.font` for headings. 464 | headingFont = 465 | 466 | # The radius used as basis for the corners of most UI elements. This can 467 | # be one of the following: "none", "small", "medium", "large", "full", 468 | # or the number in pixels or rem. For example, you can use "10px", 469 | # "0.5rem", or "2rem". To follow best practices, use rem instead of 470 | # pixels when specifying a numeric size. 471 | baseRadius = 472 | 473 | # The color of the border around elements. 474 | borderColor = 475 | 476 | # Whether to show a border around input widgets. 477 | showWidgetBorder = 478 |
479 |
480 | #### Secrets
481 |
482 | toml 483 | [secrets] 484 | 485 | # List of locations where secrets are searched. An entry can be a path to a 486 | # TOML file or directory path where Kubernetes style secrets are saved. 487 | # Order is important, import is first to last, so secrets in later files 488 | # will take precedence over earlier ones. 489 | # Default: [ <path to local environment's secrets.toml file>, <path to project's secrets.toml file>,] 490 | files = [ "~/.streamlit/secrets.toml", "~/project directory/.streamlit/secrets.toml",] 491 |
492 |
1 | --- 2 | title: st.get_option 3 | slug: /develop/api-reference/configuration/st.get_option 4 | description: st.get_option retrieves a single configuration option. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.set_option 3 | slug: /develop/api-reference/configuration/st.set_option 4 | description: st.set_option updates a single configuration option. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.set_page_config 3 | slug: /develop/api-reference/configuration/st.set_page_config 4 | description: st.set_page_config configures the default settings of the page. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: Connections and databases
3 | slug: /develop/api-reference/connections
4 | ---
5 |
6 | # Connections and databases
7 |
8 | ## Setup your connection
9 |
10 |
11 |
12 |
13 |
14 |
15 |
python 20 | conn = st.connection('pets_db', type='sql') 21 | pet_owners = conn.query('select * from pet_owners') 22 | st.dataframe(pet_owners) 23 |
24 |
25 |
26 |
27 |
28 | ## Built-in connections
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | A connection to Snowflake.
39 |
40 | python 41 | conn = st.connection('snowflake') 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | A connection to a SQL database using SQLAlchemy.
53 |
54 | python 55 | conn = st.connection('sql') 56 |
57 |
58 |
59 |
60 |
61 | ## Third-party connections
62 |
63 |
64 |
65 |
66 |
67 |
68 | Build your own connection with BaseConnection
.
69 |
70 | python 71 | class MyConnection(BaseConnection[myconn.MyConnection]): 72 | def _connect(self, **kwargs) -> MyConnection: 73 | return myconn.connect(**self._secrets, **kwargs) 74 | def query(self, query): 75 | return self._instance.query(query) 76 |
77 |
78 |
79 |
80 |
81 |
82 | ## Secrets
83 |
84 |
85 |
86 |
87 |
88 |
89 | Access secrets from a local TOML file.
90 |
91 | python 92 | key = st.secrets["OpenAI_key"] 93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | Save your secrets in a per-project or per-profile TOML file.
101 |
102 | python 103 | OpenAI_key = "<YOUR_SECRET_KEY>" 104 |
105 |
106 |
107 |
108 |
109 |
110 | ## Deprecated classes
111 |
112 |
113 |
114 |
115 |
116 |
117 | A connection to Snowflake.
118 |
119 | python 120 | conn = st.connection("snowpark") 121 |
122 |
123 |
124 |
125 |
126 |
1 | ---
2 | title: st.connection
3 | slug: /develop/api-reference/connections/st.connection
4 | ---
5 |
6 |
7 |
8 | This page only contains the st.connection
API. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
9 |
10 |
11 |
12 |
13 |
14 | For a comprehensive overview of this feature, check out this video tutorial by Joshua Carroll, Streamlit's Product Manager for Developer Experience. You'll learn about the feature's utility in creating and managing data connections within your apps by using real-world examples.
15 |
16 |
17 |
1 | ---
2 | title: st.connections.BaseConnection
3 | slug: /develop/api-reference/connections/st.connections.baseconnection
4 | ---
5 |
6 |
7 |
8 | This page only contains information on the st.connections.BaseConnection
class. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
1 | ---
2 | title: st.connections.ExperimentalBaseConnection
3 | slug: /develop/api-reference/connections/st.connections.experimentalbaseconnection
4 | ---
5 |
6 |
7 |
8 | This is an experimental feature. Experimental features and their APIs may change or be removed at any time. To learn more, click here.
9 |
10 |
11 |
12 |
13 |
14 | This page only contains information on the st.connections.ExperimentalBaseConnection
class. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
15 |
16 |
17 |
18 |
19 |
20 |
21 |
1 | ---
2 | title: st.connections.SnowflakeConnection
3 | slug: /develop/api-reference/connections/st.connections.snowflakeconnection
4 | ---
5 |
6 |
7 |
8 | This page only contains the st.connections.SnowflakeConnection
class. For a deeper dive into creating and managing data connections within Streamlit apps, see Connect Streamlit to Snowflake and Connecting to data.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
1 | ---
2 | title: st.connections.SnowparkConnection
3 | slug: /develop/api-reference/connections/st.connections.snowparkconnection
4 | ---
5 |
6 |
7 |
8 | This page only contains the st.connections.SnowparkConnection
class. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
1 | ---
2 | title: st.connections.SQLConnection
3 | slug: /develop/api-reference/connections/st.connections.sqlconnection
4 | ---
5 |
6 |
7 |
8 | This page only contains the st.connections.SQLConnection
class. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
1 | ---
2 | title: st.experimental_connection
3 | slug: /develop/api-reference/connections/st.experimental_connection
4 | ---
5 |
6 |
7 |
8 | This is an experimental feature. Experimental features and their APIs may change or be removed at any time. To learn more, click here.
9 |
10 |
11 |
12 |
13 |
14 | This page only contains the st.experimental_connection
API. For a deeper dive into creating and managing data connections within Streamlit apps, read Connecting to data.
15 |
16 |
17 |
18 |
19 |
20 | For a comprehensive overview of this feature, check out this video tutorial by Joshua Carroll, Streamlit's Product Manager for Developer Experience. You'll learn about the feature's utility in creating and managing data connections within your apps by using real-world examples.
21 |
22 |
23 |
1 | ---
2 | title: secrets.toml
3 | slug: /develop/api-reference/connections/secrets.toml
4 | ---
5 |
6 | ## secrets.toml
7 |
8 | secrets.toml
is an optional file you can define for your working directory or global development environment. When secrets.toml
is defined both globally and in your working directory, Streamlit combines the secrets and gives precendence to the working-directory secrets. For more information, see Secrets management.
9 |
10 | ### File location
11 |
12 | To define your secrets locally or per-project, add .streamlit/secrets.toml
to your working directory. Your working directory is wherever you call streamlit run
. If you haven't previously created the .streamlit
directory, you will need to add it.
13 |
14 | To define your configuration globally, you must first locate your global .streamlit
directory. Streamlit adds this hidden directory to your OS user profile during installation. For MacOS/Linux, this will be ~/.streamlit/secrets.toml
. For Windows, this will be %userprofile%/.streamlit/secrets.toml
.
15 |
16 | Optionally, you can change where Streamlit searches for secrets through the configuration option, secrets.files
.
17 |
18 | ### File format
19 |
20 | secrets.toml
is a TOML file.
21 |
22 | #### Example
23 |
24 | toml 25 | OpenAI_key = "your OpenAI key" 26 | whitelist = ["sally", "bob", "joe"] 27 | 28 | [database] 29 | user = "your username" 30 | password = "your password" 31 |
32 |
33 | In your Streamlit app, the following values would be true:
34 |
35 | python 36 | st.secrets["OpenAI_key"] == "your OpenAI key" 37 | "sally" in st.secrets.whitelist 38 | st.secrets["database"]["user"] == "your username" 39 | st.secrets.database.password == "your password" 40 |
41 |
1 | ---
2 | title: st.secrets
3 | slug: /develop/api-reference/connections/st.secrets
4 | ---
5 |
6 | ## st.secrets
7 |
8 | st.secrets
provides a dictionary-like interface to access secrets stored in a secrets.toml
file. It behaves similarly to st.session_state
. st.secrets
can be used with both key and attribute notation. For example, st.secrets.your_key
and st.secrets["your_key"]
refer to the same value. For more information about using st.secrets
, see Secrets management.
9 |
10 | ### secrets.toml
11 |
12 | By default, secrets can be saved globally or per-project. When both types of secrets are saved, Streamlit will combine the saved values but give precedence to per-project secrets if there are duplicate keys. For information on how to format and locate your secrets.toml
file for your development environment, see secrets.toml
.
13 |
14 | ### Configure secrets locations
15 |
16 | You can configure where Streamlit searches for secrets through the configuration option, secrets.files
. With this option, you can list additional secrets locations and change the order of precedence. You can specify other TOML files or include Kubernetes style secret files.
17 |
18 | #### Example
19 |
20 | toml 21 | OpenAI_key = "your OpenAI key" 22 | whitelist = ["sally", "bob", "joe"] 23 | 24 | [database] 25 | user = "your username" 26 | password = "your password" 27 |
28 |
29 | In your Streamlit app, the following values would be true:
30 |
31 | python 32 | st.secrets["OpenAI_key"] == "your OpenAI key" 33 | "sally" in st.secrets.whitelist 34 | st.secrets["database"]["user"] == "your username" 35 | st.secrets.database.password == "your password" 36 |
37 |
1 | ---
2 | title: Execution flow
3 | slug: /develop/api-reference/execution-flow
4 | ---
5 |
6 | # Execution flow
7 |
8 | ## Change execution
9 |
10 | By default, Streamlit apps execute the script entirely, but we allow some functionality to handle control flow in your applications.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
python 23 | @st.dialog("Sign up") 24 | def email_form(): 25 | name = st.text_input("Name") 26 | email = st.text_input("Email") 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Define a fragment to rerun independently from the rest of the script.
36 |
37 | python 38 | @st.fragment(run_every="10s") 39 | def fragment(): 40 | df = get_data() 41 | st.line_chart(df) 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Rerun the script immediately.
51 |
52 | python 53 | st.rerun() 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Stops execution immediately.
63 |
64 | python 65 | st.stop() 66 |
67 |
68 |
69 |
70 |
71 |
72 | ## Group multiple widgets
73 |
74 | By default, Streamlit reruns your script everytime a user interacts with your app.
75 | However, sometimes it's a better user experience to wait until a group of related
76 | widgets is filled before actually rerunning the script. That's what st.form
is for!
77 |
78 |
79 |
80 |
81 |
82 |
83 | Create a form that batches elements together with a “Submit" button.
84 |
85 | python 86 | with st.form(key='my_form'): 87 | name = st.text_input("Name") 88 | email = st.text_input("Email") 89 | st.form_submit_button("Sign up") 90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Display a form submit button.
99 |
100 | python 101 | with st.form(key='my_form'): 102 | name = st.text_input("Name") 103 | email = st.text_input("Email") 104 | st.form_submit_button("Sign up") 105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | Force a refresh without tying up a script. Created by @kmcgrady.
120 |
121 | python 122 | from streamlit_autorefresh import st_autorefresh 123 | 124 | st_autorefresh(interval=2000, limit=100, 125 | key="fizzbuzzcounter") 126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | Auto-generate Streamlit UI from Pydantic Models and Dataclasses. Created by @lukasmasuch.
137 |
138 | python 139 | import streamlit_pydantic as sp 140 | 141 | sp.pydantic_form(key="my_form", 142 | model=ExampleModel) 143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | An experimental version of Streamlit Multi-Page Apps. Created by @blackary.
154 |
155 | python 156 | from st_pages import Page, show_pages, add_page_title 157 | 158 | show_pages([ Page("streamlit_app.py", "Home", "🏠"), 159 | Page("other_pages/page2.py", "Page 2", ":books:"), ]) 160 |
161 |
162 |
163 |
164 |
165 |
1 | --- 2 | title: st.dialog 3 | slug: /develop/api-reference/execution-flow/st.dialog 4 | description: st.dialog opens a multi-element modal overlay 5 | keywords: popup, modal, overlay 6 | --- 7 | 8 | 9 |
1 | --- 2 | title: st.experimental_rerun 3 | slug: /develop/api-reference/execution-flow/st.experimental_rerun 4 | description: st.experimental_rerun will rerun the script immediately. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.form
3 | slug: /develop/api-reference/execution-flow/st.form
4 | description: st.form creates a form that batches elements together with a “Submit" button.
5 | ---
6 |
7 |
8 |
9 | This page only contains information on the st.forms
API. For a deeper dive into creating and using forms within Streamlit apps, read our guide on Using forms.
10 |
11 |
12 |
13 |
14 |
1 | --- 2 | title: st.form_submit_button 3 | slug: /develop/api-reference/execution-flow/st.form_submit_button 4 | description: st.form_submit_button displays a form submit button. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.fragment 3 | slug: /develop/api-reference/execution-flow/st.fragment 4 | description: st.fragment is a decorator that allows a function to rerun independently 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.rerun
3 | slug: /develop/api-reference/execution-flow/st.rerun
4 | description: st.rerun will rerun the script immediately.
5 | ---
6 |
7 |
8 |
9 | ### Caveats for st.rerun
10 |
11 | st.rerun
is one of the tools to control the logic of your app. While it is great for prototyping, there can be adverse side effects:
12 |
13 | - Additional script runs may be inefficient and slower.
14 | - Excessive reruns may complicate your app's logic and be harder to follow.
15 | - If misused, infinite looping may crash your app.
16 |
17 | In many cases where st.rerun
works, callbacks may be a cleaner alternative. Containers may also be helpful.
18 |
19 | ### A simple example in three variations
20 |
21 | ###### Using st.rerun
to update an earlier header
22 |
23 | python 24 | import streamlit as st 25 | 26 | if "value" not in st.session_state: 27 | st.session_state.value = "Title" 28 | 29 | ##### Option using st.rerun ##### 30 | st.header(st.session_state.value) 31 | 32 | if st.button("Foo"): 33 | st.session_state.value = "Foo" 34 | st.rerun() 35 |
36 |
37 | ###### Using a callback to update an earlier header
38 |
39 | python 40 | ##### Option using a callback ##### 41 | st.header(st.session_state.value) 42 | 43 | def update_value(): 44 | st.session_state.value = "Bar" 45 | 46 | st.button("Bar", on_click=update_value) 47 |
48 |
49 | ###### Using containers to update an earlier header
50 |
51 | python 52 | ##### Option using a container ##### 53 | container = st.container() 54 | 55 | if st.button("Baz"): 56 | st.session_state.value = "Baz" 57 | 58 | container.header(st.session_state.value) 59 |
60 |
1 | --- 2 | title: st.stop 3 | slug: /develop/api-reference/execution-flow/st.stop 4 | description: st.stop stops the execution immediately. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: Custom components 3 | slug: /develop/api-reference/custom-components 4 | --- 5 | 6 | # Custom components 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | Create and register a custom component. 15 | 16 |python 17 | from st.components.v1 import declare_component 18 | declare_component( 19 | "custom_slider", 20 | "/frontend", 21 | ) 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Display an HTML string in an iframe.
31 |
32 | python 33 | from st.components.v1 import html 34 | html( 35 | "<p>Foo bar.</p>" 36 | ) 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Load a remote URL in an iframe.
46 |
47 | python 48 | from st.components.v1 import iframe 49 | iframe( 50 | "docs.streamlit.io" 51 | ) 52 |
53 |
54 |
55 |
56 |
57 |
1 | --- 2 | title: st.components.v1.declare_component 3 | slug: /develop/api-reference/custom-components/st.components.v1.declare_component 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.components.v1.html 3 | slug: /develop/api-reference/custom-components/st.components.v1.html 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.components.v1.iframe 3 | slug: /develop/api-reference/custom-components/st.components.v1.iframe 4 | --- 5 | 6 | 7 |
1 | ---
2 | title: Data elements
3 | slug: /develop/api-reference/data
4 | ---
5 |
6 | # Data elements
7 |
8 | When you're working with data, it is extremely valuable to visualize that
9 | data quickly, interactively, and from multiple different angles. That's what
10 | Streamlit is actually built and optimized for.
11 |
12 | You can display data via charts, and you can display it in
13 | raw form. These are the Streamlit commands you can use to display and interact with raw data.
14 |
15 |
16 |
17 |
18 |
19 |
python 24 | st.dataframe(my_data_frame) 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Display a data editor widget.
35 |
36 | python 37 | edited = st.data_editor(df, num_rows="dynamic") 38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Configure the display and editing behavior of dataframes and data editors.
48 |
49 | python 50 | st.column_config.NumberColumn("Price (in USD)", min_value=0, format="$%d") 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Display a static table.
61 |
62 | python 63 | st.table(my_data_frame) 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | Display a metric in big bold font, with an optional indicator of how the metric changed.
73 |
74 | python 75 | st.metric("My metric", 42, 2) 76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Display object or string as a pretty-printed JSON string.
85 |
86 | python 87 | st.json(my_dict) 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | Implementation of Ag-Grid component for Streamlit. Created by @PablocFonseca.
102 |
103 | python 104 | df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]}) 105 | grid_return = AgGrid(df, editable=True) 106 | 107 | new_df = grid_return['data'] 108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | Streamlit Component for rendering Folium maps. Created by @randyzwitch.
119 |
120 | python 121 | m = folium.Map(location=[39.949610, -75.150282], zoom_start=16) 122 | folium.Marker([39.949610, -75.150282], popup="Liberty Bell", tooltip="Liberty Bell").add_to(m) 123 | 124 | st_data = st_folium(m, width=725) 125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | Pandas profiling component for Streamlit. Created by @okld.
136 |
137 | python 138 | df = pd.read_csv("https://storage.googleapis.com/tf-datasets/titanic/train.csv") 139 | pr = df.profile_report() 140 | 141 | st_profile_report(pr) 142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | Get the coordinates of clicks on an image. Created by @blackary.
153 |
154 | python 155 | from streamlit_image_coordinates import streamlit_image_coordinates 156 | value = streamlit_image_coordinates("https://placekitten.com/200/300") 157 | 158 | st.write(value) 159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | Make Plotly charts interactive!. Created by @null-jones.
170 |
171 | python 172 | from streamlit_plotly_events import plotly_events 173 | fig = px.line(x=[1], y=[1]) 174 | 175 | selected_points = plotly_events(fig) 176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 | A library with useful Streamlit extras. Created by @arnaudmiribel.
187 |
188 | python 189 | from streamlit_extras.metric_cards import style_metric_cards 190 | col3.metric(label="No Change", value=5000, delta=0) 191 | 192 | style_metric_cards() 193 |
194 |
195 |
196 |
197 |
198 |
1 | ---
2 | title: st.column_config
3 | slug: /develop/api-reference/data/st.column_config
4 | ---
5 |
6 | # Column configuration
7 |
8 | When working with data in Streamlit, the st.column_config
class is a powerful tool for configuring data display and interaction. Specifically designed for the column_config
parameter in st.dataframe
and st.data_editor
, it provides a suite of methods to tailor your columns to various data types - from simple text and numbers to lists, URLs, images, and more.
9 |
10 | Whether it's translating temporal data into user-friendly formats or utilizing charts and progress bars for clearer data visualization, column configuration not only provides the user with an enriched data viewing experience but also ensures that you're equipped with the tools to present and interact with your data, just the way you want it.
11 |
12 |
13 |
14 |
15 |
16 |
python 21 | Column("Streamlit Widgets", width="medium", help="Streamlit **widget** commands 🎈") 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Configure a text column.
31 |
32 | python 33 | TextColumn("Widgets", max_chars=50, validate="^st\.[a-z_]+ quot;) 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Configure a number column.
44 |
45 | python 46 | NumberColumn("Price (in USD)", min_value=0, format="$%d") 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Configure a checkbox column.
57 |
58 | python 59 | CheckboxColumn("Your favorite?", help="Select your **favorite** widgets") 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Configure a selectbox column.
70 |
71 | python 72 | SelectboxColumn("App Category", options=["🤖 LLM", "📈 Data Viz"]) 73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Configure a datetime column.
83 |
84 | python 85 | DatetimeColumn("Appointment", min_value=datetime(2023, 6, 1), format="D MMM YYYY, h:mm a") 86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Configure a date column.
96 |
97 | python 98 | DateColumn("Birthday", max_value=date(2005, 1, 1), format="DD.MM.YYYY") 99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Configure a time column.
109 |
110 | python 111 | TimeColumn("Appointment", min_value=time(8, 0, 0), format="hh:mm a") 112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | Configure a JSON column.
121 |
122 | python 123 | JSONColumn("Properties", width="medium") 124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | Configure a list column.
133 |
134 | python 135 | ListColumn("Sales (last 6 months)", width="medium") 136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | Configure a link column.
146 |
147 | python 148 | LinkColumn("Trending apps", max_chars=100, validate="^https://.* quot;) 149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | Configure an image column.
159 |
160 | python 161 | ImageColumn("Preview Image", help="The preview screenshots") 162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | Configure an area chart column.
172 |
173 | python 174 | AreaChartColumn("Sales (last 6 months)" y_min=0, y_max=100) 175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | Configure a line chart column.
185 |
186 | python 187 | LineChartColumn("Sales (last 6 months)" y_min=0, y_max=100) 188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | Configure a bar chart column.
198 |
199 | python 200 | BarChartColumn("Marketing spend" y_min=0, y_max=100) 201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | Configure a progress column.
211 |
212 | python 213 | ProgressColumn("Sales volume", min_value=0, max_value=1000, format="$%f") 214 |
215 |
216 |
217 |
218 |
219 |
1 | --- 2 | title: st.column_config.AreaChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.areachartcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.BarChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.barchartcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.CheckboxColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.checkboxcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.Column 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.column 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.DateColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.datecolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.DatetimeColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.datetimecolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.ImageColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.imagecolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.JsonColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.jsoncolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.LineChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.linechartcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.LinkColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.linkcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.ListColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.listcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.NumberColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.numbercolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.ProgressColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.progresscolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.SelectboxColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.selectboxcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.TextColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.textcolumn 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.column_config.TimeColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.timecolumn 4 | --- 5 | 6 | 7 |
1 | ---
2 | title: st.data_editor
3 | slug: /develop/api-reference/data/st.data_editor
4 | description: st.data_editor display a data editor widget that allows you to edit dataframes and many other data structures in a table-like UI.
5 | ---
6 |
7 |
8 |
9 | This page only contains information on the st.data_editor
API. For an overview of working with dataframes and to learn more about the data editor's capabilities and limitations, read Dataframes.
10 |
11 |
12 |
13 |
14 |
15 | ### Configuring columns
16 |
17 | You can configure the display and editing behavior of columns in st.dataframe
and st.data_editor
via the Column configuration API. We have developed the API to let you add images, charts, and clickable URLs in dataframe and data editor columns. Additionally, you can make individual columns editable, set columns as categorical and specify which options they can take, hide the index of the dataframe, and much more.
18 |
19 |
20 |
1 | ---
2 | title: st.dataframe
3 | slug: /develop/api-reference/data/st.dataframe
4 | description: st.dataframe displays a dataframe as an interactive table.
5 | ---
6 |
7 |
8 |
9 | Learn more in our Dataframes guide and check out our tutorial, Get dataframe row-selections from users.
10 |
11 |
12 |
13 |
14 |
15 | ## Dataframe selections
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## Interactivity
24 |
25 | Dataframes displayed with st.dataframe
are interactive. End users can sort, resize, search, and copy data to their clipboard. For on overview of features, read our Dataframes guide.
26 |
27 | ## Configuring columns
28 |
29 | You can configure the display and editing behavior of columns in st.dataframe
and st.data_editor
via the Column configuration API. We have developed the API to let you add images, charts, and clickable URLs in dataframe and data editor columns. Additionally, you can make individual columns editable, set columns as categorical and specify which options they can take, hide the index of the dataframe, and much more.
30 |
31 |
32 |
1 | --- 2 | title: st.experimental_data_editor 3 | slug: /develop/api-reference/data/st.experimental_data_editor 4 | description: st.experimental_data_editor display a data editor widget that allows you to edit dataframes and many other data structures in a table-like UI. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.json 3 | slug: /develop/api-reference/data/st.json 4 | description: st.json displays object or string as a pretty-printed JSON string. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.metric 3 | slug: /develop/api-reference/data/st.metric 4 | description: st.metric displays a metric in big bold font, with an optional indicator of how the metric changed. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.table
3 | slug: /develop/api-reference/data/st.table
4 | description: st.table displays a static table.
5 | ---
6 |
7 |
8 |
9 | Static tables with st.table
are the most basic way to display dataframes. For the majority of cases, we recommend using st.dataframe
to display interactive dataframes, and st.data_editor
to let users edit dataframes.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
1 | ---
2 | title: Layouts and Containers
3 | slug: /develop/api-reference/layout
4 | ---
5 |
6 | # Layouts and Containers
7 |
8 | ## Complex layouts
9 |
10 | Streamlit provides several options for controlling how different elements are laid out on the screen.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
python 22 | col1, col2 = st.columns(2) 23 | col1.write("this is column 1") 24 | col2.write("this is column 2") 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Insert a multi-element container.
35 |
36 | python 37 | c = st.container() 38 | st.write("This will show last") 39 | c.write("This will show first") 40 | c.write("This will show second") 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Insert a modal dialog that can rerun independently from the rest of the script.
51 |
52 | python 53 | @st.dialog("Sign up") 54 | def email_form(): 55 | name = st.text_input("Name") 56 | email = st.text_input("Email") 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Insert a single-element container.
67 |
68 | python 69 | c = st.empty() 70 | st.write("This will show last") 71 | c.write("This will be replaced") 72 | c.write("This will show first") 73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Insert a multi-element container that can be expanded/collapsed.
83 |
84 | python 85 | with st.expander("Open to see more"): 86 | st.write("This is more content") 87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | Insert a multi-element popover container that can be opened/closed.
97 |
98 | python 99 | with st.popover("Settings"): 100 | st.checkbox("Show completed") 101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Display items in a sidebar.
111 |
112 | python 113 | st.sidebar.write("This lives in the sidebar") 114 | st.sidebar.button("Click me!") 115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | Insert containers separated into tabs.
125 |
126 | python 127 | tab1, tab2 = st.tabs(["Tab 1", "Tab2"]) 128 | tab1.write("this is tab 1") 129 | tab2.write("this is tab 2") 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | Create a draggable and resizable dashboard in Streamlit. Created by @okls.
144 |
145 | python 146 | from streamlit_elements import elements, mui, html 147 | 148 | with elements("new_element"): 149 | mui.Typography("Hello world") 150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | Auto-generate Streamlit UI from Pydantic Models and Dataclasses. Created by @lukasmasuch.
161 |
162 | python 163 | import streamlit_pydantic as sp 164 | 165 | sp.pydantic_form(key="my_form", 166 | model=ExampleModel) 167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | An experimental version of Streamlit Multi-Page Apps. Created by @blackary.
178 |
179 | python 180 | from st_pages import Page, show_pages, add_page_title 181 | 182 | show_pages([ Page("streamlit_app.py", "Home", "🏠"), 183 | Page("other_pages/page2.py", "Page 2", ":books:"), ]) 184 |
185 |
186 |
187 |
188 |
189 |
1 | --- 2 | title: st.columns 3 | slug: /develop/api-reference/layout/st.columns 4 | description: st.columns inserts containers laid out as side-by-side columns. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.container 3 | slug: /develop/api-reference/layout/st.container 4 | description: st.container inserts a multi-element container. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.empty 3 | slug: /develop/api-reference/layout/st.empty 4 | description: st.empty inserts a single-element container. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.expander 3 | slug: /develop/api-reference/layout/st.expander 4 | description: st.expander inserts a multi-element container that can be expanded/collapsed. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.popover 3 | slug: /develop/api-reference/layout/st.popover 4 | description: st.popover inserts a multi-element popover container 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.sidebar
3 | slug: /develop/api-reference/layout/st.sidebar
4 | description: st.sidebar displays items in a sidebar.
5 | ---
6 |
7 | ## st.sidebar
8 |
9 | ## Add widgets to sidebar
10 |
11 | Not only can you add interactivity to your app with widgets, you can organize them into a sidebar. Elements can be passed to st.sidebar
using object notation and with
notation.
12 |
13 | The following two snippets are equivalent:
14 |
15 | python 16 | # Object notation 17 | st.sidebar.[element_name] 18 |
19 |
20 | python 21 | # "with" notation 22 | with st.sidebar: 23 | st.[element_name] 24 |
25 |
26 | Each element that's passed to st.sidebar
is pinned to the left, allowing users to focus on the content in your app.
27 |
28 |
29 |
30 | The sidebar is resizable! Drag and drop the right border of the sidebar to resize it! python 37 | import streamlit as st 38 | 39 | # Using object notation 40 | add_selectbox = st.sidebar.selectbox( 41 | "How would you like to be contacted?", 42 | ("Email", "Home phone", "Mobile phone") 43 | ) 44 | 45 | # Using "with" notation 46 | with st.sidebar: 47 | add_radio = st.radio( 48 | "Choose a shipping method", 49 | ("Standard (5-15 days)", "Express (2-5 days)") 50 | ) 51 |
52 |
53 |
54 |
55 | The only elements that aren't supported using object notation are st.echo
, st.spinner
, and st.toast
. To use these elements, you must use with
notation.
56 |
57 |
58 |
59 | Here's an example of how you'd add st.echo
and st.spinner
to your sidebar:
60 |
61 | python 62 | import streamlit as st 63 | import time 64 | 65 | with st.sidebar: 66 | with st.echo(): 67 | st.write("This code will be printed to the sidebar.") 68 | 69 | with st.spinner("Loading..."): 70 | time.sleep(5) 71 | st.success("Done!") 72 |
73 |
1 | --- 2 | title: st.tabs 3 | slug: /develop/api-reference/layout/st.tabs 4 | description: st.tabs inserts containers separated into tabs. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: Media elements
3 | slug: /develop/api-reference/media
4 | ---
5 |
6 | # Media elements
7 |
8 | It's easy to embed images, videos, and audio files directly into your Streamlit apps.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
python 20 | st.image(numpy_array) 21 | st.image(image_bytes) 22 | st.image(file) 23 | st.image("https://example.com/myimage.jpg") 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Display a logo in the upper-left corner of your app and its sidebar.
34 |
35 | python 36 | st.logo("logo.jpg") 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Display an audio player.
47 |
48 | python 49 | st.audio(numpy_array) 50 | st.audio(audio_bytes) 51 | st.audio(file) 52 | st.audio("https://example.com/myaudio.mp3", format="audio/mp3") 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Display a video player.
63 |
64 | python 65 | st.video(numpy_array) 66 | st.video(video_bytes) 67 | st.video(file) 68 | st.video("https://example.com/myvideo.mp4", format="video/mp4") 69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | Handling and transmitting real-time video/audio streams with Streamlit. Created by @whitphx.
83 |
84 | python 85 | from streamlit_webrtc import webrtc_streamer 86 | 87 | webrtc_streamer(key="sample") 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Provides a sketching canvas using Fabric.js. Created by @andfanilo.
99 |
100 | python 101 | from streamlit_drawable_canvas import st_canvas 102 | 103 | st_canvas(fill_color="rgba(255, 165, 0, 0.3)", stroke_width=stroke_width, stroke_color=stroke_color, background_color=bg_color, background_image=Image.open(bg_image) if bg_image else None, update_streamlit=realtime_update, height=150, drawing_mode=drawing_mode, point_display_radius=point_display_radius if drawing_mode == 'point' else 0, key="canvas",) 104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | Compare images with a slider using JuxtaposeJS. Created by @fcakyon.
115 |
116 | python 117 | from streamlit_image_comparison import image_comparison 118 | 119 | image_comparison(img1="image1.jpg", img2="image2.jpg",) 120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | A simple image cropper for Streamlit. Created by @turner-anderson.
131 |
132 | python 133 | from streamlit_cropper import st_cropper 134 | 135 | st_cropper(img, realtime_update=realtime_update, box_color=box_color, aspect_ratio=aspect_ratio) 136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | Get the coordinates of clicks on an image. Created by @blackary.
147 |
148 | python 149 | from streamlit_image_coordinates import streamlit_image_coordinates 150 | 151 | streamlit_image_coordinates("https://placekitten.com/200/300") 152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | Integrate Lottie animations inside your Streamlit app. Created by @andfanilo.
163 |
164 | python 165 | lottie_hello = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_V9t630.json") 166 | 167 | st_lottie(lottie_hello, key="hello") 168 |
169 |
170 |
171 |
172 |
173 |
1 | --- 2 | title: st.audio 3 | slug: /develop/api-reference/media/st.audio 4 | description: st.audio displays an audio player. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.image 3 | slug: /develop/api-reference/media/st.image 4 | description: st.image displays an image or list of images. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.logo 3 | slug: /develop/api-reference/media/st.logo 4 | description: st.logo displays an image in the upper-left corner of your app and its sidebar. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.video 3 | slug: /develop/api-reference/media/st.video 4 | description: st.video displays a video player. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: Navigation and pages
3 | slug: /develop/api-reference/navigation
4 | ---
5 |
6 | # Navigation and pages
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
python 19 | st.navigation({ 20 | "Your account" : [log_out, settings], 21 | "Reports" : [overview, usage], 22 | "Tools" : [search] 23 | }) 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Define a page in a multipage app.
35 |
36 | python 37 | home = st.Page( 38 | "home.py", 39 | title="Home", 40 | icon=":material/home:" 41 | ) 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Display a link to another page in a multipage app.
53 |
54 | python 55 | st.page_link("app.py", label="Home", icon="🏠") 56 | st.page_link("pages/profile.py", label="Profile") 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Programmatically navigates to a specified page.
66 |
67 | python 68 | st.switch_page("pages/my_page.py") 69 |
70 |
71 |
72 |
73 |
74 |
1 | --- 2 | title: st.navigation 3 | slug: /develop/api-reference/navigation/st.navigation 4 | description: st.navigation declares the set of available pages to select in a multipage app 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.Page 3 | slug: /develop/api-reference/navigation/st.page 4 | description: st.Page initializes a StreamlitPage object for multipage apps 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1 | --- 2 | title: st.switch_page 3 | slug: /develop/api-reference/navigation/st.switch_page 4 | description: st.switch_page programmatically switches the active page. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: Display progress and status
3 | slug: /develop/api-reference/status
4 | ---
5 |
6 | # Display progress and status
7 |
8 | Streamlit provides a few methods that allow you to add animation to your
9 | apps. These animations include progress bars, status messages (like
10 | warnings), and celebratory balloons.
11 |
12 | ## Animated status elements
13 |
14 |
15 |
16 |
17 |
18 |
19 |
python 24 | for i in range(101): 25 | st.progress(i) 26 | do_something_slow() 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Temporarily displays a message while executing a block of code.
37 |
38 | python 39 | with st.spinner("Please wait..."): 40 | do_something_slow() 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Display output of long-running tasks in a container.
51 |
52 | python 53 | with st.status('Running'): 54 | do_something_slow() 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Briefly displays a toast message in the bottom-right corner.
65 |
66 | python 67 | st.toast('Butter!', icon='🧈') 68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Display celebratory balloons!
78 |
79 | python 80 | st.balloons() 81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | Display celebratory snowflakes!
91 |
92 | python 93 | st.snow() 94 |
95 |
96 |
97 |
98 |
99 | ## Simple callout messages
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Display a success message.
109 |
110 | python 111 | st.success("Match found!") 112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | Display an informational message.
122 |
123 | python 124 | st.info("Dataset is updated every day at midnight.") 125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | Display warning message.
135 |
136 | python 137 | st.warning("Unable to fetch image. Skipping...") 138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | Display error message.
148 |
149 | python 150 | st.error("We encountered an error") 151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | Display an exception.
161 |
162 | python 163 | e = RuntimeError("This is an exception of type RuntimeError") 164 | st.exception(e) 165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 | The simplest way to handle a progress bar in streamlit app. Created by @Wirg.
179 |
180 | python 181 | from stqdm import stqdm 182 | 183 | for _ in stqdm(range(50)): 184 | sleep(0.5) 185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | A custom notification box with the ability to close it out. Created by @Socvest.
196 |
197 | python 198 | from streamlit_custom_notification_box import custom_notification_box 199 | 200 | styles = {'material-icons':{'color': 'red'}, 'text-icon-link-close-container': {'box-shadow': '#3896de 0px 4px'}, 'notification-text': {'':''}, 'close-button':{'':''}, 'link':{'':''}} 201 | custom_notification_box(icon='info', textDisplay='We are almost done with your registration...', externalLink='more info', url='#', styles=styles, key="foo") 202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 | A library with useful Streamlit extras. Created by @arnaudmiribel.
213 |
214 | python 215 | from streamlit_extras.let_it_rain import rain 216 | 217 | rain(emoji="🎈", font_size=54, 218 | falling_speed=5, animation_length="infinite",) 219 |
220 |
221 |
222 |
223 |
224 |
1 | --- 2 | title: st.balloons 3 | slug: /develop/api-reference/status/st.balloons 4 | description: st.balloons displays celebratory balloons! 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.error 3 | slug: /develop/api-reference/status/st.error 4 | description: st.error displays error message. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.exception 3 | slug: /develop/api-reference/status/st.exception 4 | description: st.exception displays an exception. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.info 3 | slug: /develop/api-reference/status/st.info 4 | description: st.info displays an informational message. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.progress 3 | slug: /develop/api-reference/status/st.progress 4 | description: st.progress displays a progress bar. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.snow 3 | slug: /develop/api-reference/status/st.snow 4 | description: st.snow displays celebratory snowflakes! 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.spinner 3 | slug: /develop/api-reference/status/st.spinner 4 | description: st.spinner temporarily displays a message while executing a block of code. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.status 3 | slug: /develop/api-reference/status/st.status 4 | description: st.status inserts a mutable expander element 5 | --- 6 | 7 | 8 | 9 | 10 |
1 | --- 2 | title: st.success 3 | slug: /develop/api-reference/status/st.success 4 | description: st.success displays a success message. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.toast
3 | slug: /develop/api-reference/status/st.toast
4 | description: st.toast briefly displays a toast message in the bottom-right corner
5 | ---
6 |
7 |
8 |
9 | When multiple toasts are generated, they will stack. Hovering over a toast will
10 | stop it from disappearing. When hovering ends, the toast will disappear after
11 | four more seconds.
12 |
13 | python 14 | import streamlit as st 15 | import time 16 | 17 | if st.button('Three cheers'): 18 | st.toast('Hip!') 19 | time.sleep(.5) 20 | st.toast('Hip!') 21 | time.sleep(.5) 22 | st.toast('Hooray!', icon='🎉') 23 |
24 |
25 |
26 |
27 | Toast messages can also be updated. Assign st.toast(my_message)
to a variable
28 | and use the .toast()
method to update it. Note: if a toast has already disappeared
29 | or been dismissed, the update will not be seen.
30 |
31 | python 32 | import streamlit as st 33 | import time 34 | 35 | def cook_breakfast(): 36 | msg = st.toast('Gathering ingredients...') 37 | time.sleep(1) 38 | msg.toast('Cooking...') 39 | time.sleep(1) 40 | msg.toast('Ready!', icon = "🥞") 41 | 42 | if st.button('Cook breakfast'): 43 | cook_breakfast() 44 |
45 |
46 |
47 |
1 | --- 2 | title: st.warning 3 | slug: /develop/api-reference/status/st.warning 4 | description: st.warning displays warning message. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: App testing 3 | slug: /develop/api-reference/app-testing 4 | --- 5 | 6 | # App testing 7 | 8 | Streamlit app testing framework enables developers to build and run headless tests that execute their app code directly, simulate user input, and inspect rendered outputs for correctness. 9 | 10 | The provided class, AppTest, simulates a running app and provides methods to set up, manipulate, and inspect the app contents via API instead of a browser UI. It can be used to write automated tests of an app in various scenarios. These can then be run using a tool like pytest. A typical pattern is to build a suite of tests for an app that ensure consistent functionality as the app evolves, and run the tests locally and/or in a CI environment like Github Actions. 11 | 12 | ## The AppTest class 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |st.testing.v1.AppTest
simulates a running Streamlit app for testing.
21 |
22 | python 23 | from streamlit.testing.v1 import AppTest 24 | 25 | at = AppTest.from_file("streamlit_app.py") 26 | at.secrets["WORD"] = "Foobar" 27 | at.run() 28 | assert not at.exception 29 | 30 | at.text_input("word").input("Bazbat").run() 31 | assert at.warning[0].value == "Try again." 32 |
33 |
34 |
35 |
36 |
37 |
38 | {/** TODO: Bug fix. The second RefCard does not render. Empty card is a workaround. **/}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | st.testing.v1.AppTest.from_file
initializes a simulated app from a file.
47 |
48 | python 49 | from streamlit.testing.v1 import AppTest 50 | 51 | at = AppTest.from_file("streamlit_app.py") 52 | at.secrets["WORD"] = "Foobar" 53 | at.run() 54 | assert not at.exception 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | st.testing.v1.AppTest.from_string
initializes a simulated app from a string.
64 |
65 | python 66 | from streamlit.testing.v1 import AppTest 67 | 68 | app_script = """ 69 | import streamlit as st 70 | 71 | word_of_the_day = st.text_input("What's the word of the day?", key="word") 72 | if word_of_the_day == st.secrets["WORD"]: 73 | st.success("That's right!") 74 | elif word_of_the_day and word_of_the_day != st.secrets["WORD"]: 75 | st.warn("Try again.") 76 | """ 77 | 78 | at = AppTest.from_string(app_script) 79 | at.secrets["WORD"] = "Foobar" 80 | at.run() 81 | assert not at.exception 82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | st.testing.v1.AppTest.from_function
initializes a simulated app from a function.
91 |
92 | python 93 | from streamlit.testing.v1 import AppTest 94 | 95 | def app_script (): 96 | import streamlit as st 97 | 98 | word_of_the_day = st.text_input("What's the word of the day?", key="word") 99 | if word_of_the_day == st.secrets["WORD"]: 100 | st.success("That's right!") 101 | elif word_of_the_day and word_of_the_day != st.secrets["WORD"]: 102 | st.warn("Try again.") 103 | 104 | at = AppTest.from_function(app_script) 105 | at.secrets["WORD"] = "Foobar" 106 | at.run() 107 | assert not at.exception 108 |
109 |
110 |
111 |
112 |
113 |
114 | ## Testing-element classes
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | A representation of container elements, including:
123 |
124 | - st.chat_message
125 | - st.columns
126 | - st.sidebar
127 | - st.tabs
128 | - The main body of the app.
129 |
130 | python 131 | # at.sidebar returns a Block 132 | at.sidebar.button[0].click().run() 133 | assert not at.exception 134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | The base class for representation of all elements, including:
143 |
144 | - st.title
145 | - st.header
146 | - st.markdown
147 | - st.dataframe
148 |
149 | python 150 | # at.title returns a sequence of Title 151 | # Title inherits from Element 152 | assert at.title[0].value == "My awesome app" 153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | A representation of st.button
and st.form_submit_button
.
162 |
163 | python 164 | at.button[0].click().run() 165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | A representation of st.chat_input
.
174 |
175 | python 176 | at.chat_input[0].set_value("What is Streamlit?").run() 177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | A representation of st.checkbox
.
186 |
187 | python 188 | at.checkbox[0].check().run() 189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | A representation of st.color_picker
.
198 |
199 | python 200 | at.color_picker[0].pick("#FF4B4B").run() 201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | A representation of st.date_input
.
210 |
211 | python 212 | release_date = datetime.date(2023, 10, 26) 213 | at.date_input[0].set_value(release_date).run() 214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | A representation of st.multiselect
.
223 |
224 | python 225 | at.multiselect[0].select("New York").run() 226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | A representation of st.number_input
.
235 |
236 | python 237 | at.number_input[0].increment().run() 238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 | A representation of st.radio
.
247 |
248 | python 249 | at.radio[0].set_value("New York").run() 250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 | A representation of st.select_slider
.
259 |
260 | python 261 | at.select_slider[0].set_range("A","C").run() 262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 | A representation of st.selectbox
.
271 |
272 | python 273 | at.selectbox[0].select("New York").run() 274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 | A representation of st.slider
.
283 |
284 | python 285 | at.slider[0].set_range(2,5).run() 286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | A representation of st.text_area
.
295 |
296 | python 297 | at.text_area[0].input("Streamlit is awesome!").run() 298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 | A representation of st.text_input
.
307 |
308 | python 309 | at.text_input[0].input("Streamlit").run() 310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 | A representation of st.time_input
.
319 |
320 | python 321 | at.time_input[0].increment().run() 322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 | A representation of st.toggle
.
331 |
332 | python 333 | at.toggle[0].set_value("True").run() 334 |
335 |
336 |
337 |
338 |
339 |
1 | ---
2 | title: st.testing.v1.AppTest
3 | slug: /develop/api-reference/app-testing/st.testing.v1.apptest
4 | ---
5 |
6 | <h1 style={{display: "none"}}>
7 |
8 | # The AppTest class
9 |
10 |
11 |
12 | # Initialize a simulated app using AppTest
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | # Run an AppTest script
21 |
22 |
23 |
24 |
25 |
26 | # Get AppTest script elements
27 |
28 | The main value of AppTest
is providing an API to programmatically inspect and interact with the elements and widgets produced by a running Streamlit app. Using the AppTest.<element type>
properties or AppTest.get()
method returns a collection of all the elements or widgets of the specified type that would have been displayed by running the app.
29 |
30 | Note that you can also retrieve elements within a specific container in the same way - first retrieve the container, then retrieve the elements just in that container.
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
1 | ---
2 | title: Testing element classes
3 | slug: /develop/api-reference/app-testing/testing-element-classes
4 | ---
5 |
6 | # Testing element classes
7 |
8 | ## st.testing.v1.element_tree.Block
9 |
10 | The Block
class has the same methods and attributes as AppTest
. A Block
instance represents a container of elements just as AppTest
represents the entire app. For example, Block.button
will produce a WidgetList
of Button
in the same manner as AppTest.button
.
11 |
12 | ChatMessage
, Column
, and Tab
all inherit from Block
. For all container classes, parameters of the original element can be obtained as properties. For example, ChatMessage.avatar
and Tab.label
.
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
1 | ---
2 | title: Text elements
3 | slug: /develop/api-reference/text
4 | ---
5 |
6 | # Text elements
7 |
8 | Streamlit apps usually start with a call to st.title
to set the
9 | app's title. After that, there are 2 heading levels you can use:
10 | st.header
and st.subheader
.
11 |
12 | Pure text is entered with st.text
, and Markdown with
13 | st.markdown
.
14 |
15 | We also offer a "swiss-army knife" command called st.write
, which accepts
16 | multiple arguments, and multiple data types. And as described above, you can
17 | also use magic commands in place of st.write
.
18 |
19 | ## Headings and body text
20 |
21 |
22 |
23 |
24 |
25 |
26 |
python 31 | st.markdown("Hello **world**!") 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Display text in title formatting.
42 |
43 | python 44 | st.title("The app title") 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | Display text in header formatting.
55 |
56 | python 57 | st.header("This is a header") 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Display text in subheader formatting.
68 |
69 | python 70 | st.subheader("This is a subheader") 71 |
72 |
73 |
74 |
75 |
76 | ## Formatted text
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | Display a small, colored badge.
87 |
88 | python 89 | st.badge("New") 90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | Display text in small font.
100 |
101 | python 102 | st.caption("This is written small caption text") 103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | Display a code block with optional syntax highlighting.
113 |
114 | python 115 | st.code("a = 1234") 116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | Display some code on the app, then execute it. Useful for tutorials.
126 |
127 | python 128 | with st.echo(): 129 | st.write('This code will be printed') 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | Write fixed-width and preformatted text.
140 |
141 | python 142 | st.text("Hello world") 143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | Display mathematical expressions formatted as LaTeX.
153 |
154 | python 155 | st.latex("\int a x^2 \,dx") 156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | Display a horizontal rule.
166 |
167 | python 168 | st.divider() 169 |
170 |
171 |
172 |
173 |
174 | ## Utilities
175 |
176 |
177 |
178 |
179 |
180 |
181 | Display object’s doc string, nicely formatted.
182 |
183 | python 184 | st.help(st.write) 185 | st.help(pd.DataFrame) 186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | Renders HTML strings to your app.
194 |
195 | python 196 | st.html("<p>Foo bar.</p>") 197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | Display annotated text in Streamlit apps. Created by @tvst.
210 |
211 | python 212 | annotated_text("This ", ("is", "verb"), " some ", ("annotated", "adj"), ("text", "noun"), " for those of ", ("you", "pronoun"), " who ", ("like", "verb"), " this sort of ", ("thing", "noun"), ".") 213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 | Provides a sketching canvas using Fabric.js. Created by @andfanilo.
224 |
225 | python 226 | st_canvas(fill_color="rgba(255, 165, 0, 0.3)", stroke_width=stroke_width, stroke_color=stroke_color, background_color=bg_color, background_image=Image.open(bg_image) if bg_image else None, update_streamlit=realtime_update, height=150, drawing_mode=drawing_mode, point_display_radius=point_display_radius if drawing_mode == 'point' else 0, key="canvas",) 227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 | Add tags to your Streamlit apps. Created by @gagan3012.
238 |
239 | python 240 | st_tags(label='# Enter Keywords:', text='Press enter to add more', value=['Zero', 'One', 'Two'], suggestions=['five', 'six', 'seven', 'eight', 'nine', 'three', 'eleven', 'ten', 'four'], maxtags = 4, key='1') 241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 | Apply text mining on a dataframe. Created by @JohnSnowLabs.
252 |
253 | python 254 | nlu.load('sentiment').predict('I love NLU! <3') 255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | A library with useful Streamlit extras. Created by @arnaudmiribel.
266 |
267 | python 268 | mention(label="An awesome Streamlit App", icon="streamlit", url="https://extras.streamlit.app",) 269 |
270 |
271 |
272 |
273 |
1 | --- 2 | title: st.badge 3 | slug: /develop/api-reference/text/st.badge 4 | description: st.badge displays a colored badge or tag. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.caption
3 | slug: /develop/api-reference/text/st.caption
4 | description: st.caption displays text in small font.
5 | ---
6 |
7 |
8 |
9 |
10 |
1 | --- 2 | title: st.code 3 | slug: /develop/api-reference/text/st.code 4 | description: st.code displays a code block with optional syntax highlighting. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.divider
3 | slug: /develop/api-reference/text/st.divider
4 | description: st.divider displays a horizontal rule in your app.
5 | ---
6 |
7 |
8 |
9 | Here's what it looks like in action when you have multiple elements in the app:
10 |
11 | python 12 | import streamlit as st 13 | 14 | st.write("This is some text.") 15 | 16 | st.slider("This is a slider", 0, 100, (25, 75)) 17 | 18 | st.divider() # 👈 Draws a horizontal rule 19 | 20 | st.write("This text is between the horizontal rules.") 21 | 22 | st.divider() # 👈 Another horizontal rule 23 |
24 |
25 |
26 |
1 | ---
2 | title: st.echo
3 | slug: /develop/api-reference/text/st.echo
4 | description: st.echo displays some code on the app, then execute it. Useful for tutorials.
5 | ---
6 |
7 |
8 |
9 | ### Display code
10 |
11 | Sometimes you want your Streamlit app to contain both your usual
12 | Streamlit graphic elements and the code that generated those elements.
13 | That's where st.echo()
comes in.
14 |
15 | Ok so let's say you have the following file, and you want to make its
16 | app a little bit more self-explanatory by making that middle section
17 | visible in the Streamlit app:
18 |
19 | python 20 | import streamlit as st 21 | 22 | def get_user_name(): 23 | return 'John' 24 | 25 | # ------------------------------------------------ 26 | # Want people to see this part of the code... 27 | 28 | def get_punctuation(): 29 | return '!!!' 30 | 31 | greeting = "Hi there, " 32 | user_name = get_user_name() 33 | punctuation = get_punctuation() 34 | 35 | st.write(greeting, user_name, punctuation) 36 | 37 | # ...up to here 38 | # ------------------------------------------------ 39 | 40 | foo = 'bar' 41 | st.write('Done!') 42 |
43 |
44 | The file above creates a Streamlit app containing the words "Hi there,
45 | John
", and then "Done!".
46 |
47 | Now let's use st.echo()
to make that middle section of the code visible
48 | in the app:
49 |
50 | python 51 | import streamlit as st 52 | 53 | def get_user_name(): 54 | return 'John' 55 | 56 | with st.echo(): 57 | # Everything inside this block will be both printed to the screen 58 | # and executed. 59 | 60 | def get_punctuation(): 61 | return '!!!' 62 | 63 | greeting = "Hi there, " 64 | value = get_user_name() 65 | punctuation = get_punctuation() 66 | 67 | st.write(greeting, value, punctuation) 68 | 69 | # And now we're back to _not_ printing to the screen 70 | foo = 'bar' 71 | st.write('Done!') 72 |
73 |
74 | It's that simple!
75 |
76 |
77 |
78 | You can have multiple st.echo()
blocks in the same file.
79 | Use it as often as you wish!
80 |
81 |
82 |
1 | --- 2 | title: st.header 3 | slug: /develop/api-reference/text/st.header 4 | description: st.header displays text in header formatting. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.help 3 | slug: /develop/api-reference/text/st.help 4 | description: st.help displays object's doc string, nicely formatted. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.html 3 | slug: /develop/api-reference/text/st.html 4 | description: st.html renders arbitrary HTML strings to your app 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.latex
3 | slug: /develop/api-reference/text/st.latex
4 | description: st.latex displays mathematical expressions formatted as LaTeX.
5 | ---
6 |
7 |
8 |
9 |
10 |
1 | ---
2 | title: st.markdown
3 | slug: /develop/api-reference/text/st.markdown
4 | description: st.markdown displays string formatted as Markdown.
5 | ---
6 |
7 |
8 |
9 | python 10 | import streamlit as st 11 | 12 | md = st.text_area('Type in your markdown string (without outer quotes)', 13 | "Happy Streamlit-ing! :balloon:") 14 | 15 | st.code(f""" 16 | import streamlit as st 17 | 18 | st.markdown('''{md}''') 19 | """) 20 | 21 | st.markdown(md) 22 |
23 |
24 |
25 |
1 | --- 2 | title: st.subheader 3 | slug: /develop/api-reference/text/st.subheader 4 | description: st.subheader displays text in subheader formatting. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.text 3 | slug: /develop/api-reference/text/st.text 4 | description: st.text writes fixed-width and preformatted text. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.title 3 | slug: /develop/api-reference/text/st.title 4 | description: st.title displays text in title formatting. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: Authentication and user info 3 | slug: /develop/api-reference/user 4 | --- 5 | 6 | # Authentication and user info 7 | 8 | Streamlit provides native support for user authentication so you can personalize your apps. You can also directly read headers and cookies. 9 | 10 | 11 | 12 | 13 |
14 | 15 |st.login()
starts an authentication flow with an identity provider.
16 |
17 | python 18 | st.login() 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | st.logout()
removes a user's identity information.
27 |
28 | python 29 | st.logout() 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | st.user
returns information about a logged-in user.
38 |
39 | python 40 | if st.user.is_logged_in: 41 | st.write(f"Welcome back, {st.user.name}!") 42 |
43 |
44 |
45 |
46 |
1 | --- 2 | title: st.login 3 | slug: /develop/api-reference/user/st.login 4 | description: st.login redirects the user to the configured authentication provider to log in. 5 | --- 6 | 7 | 8 | 9 | Learn more in User authentication and information. 10 | 11 | 12 | 13 | 14 |
1 | --- 2 | title: st.logout 3 | slug: /develop/api-reference/user/st.logout 4 | description: st.logout removes the user's identity information and starts a clean session. 5 | --- 6 | 7 | 8 | 9 | Learn more in User authentication and information. 10 | 11 | 12 | 13 | 14 |
1 | ---
2 | title: st.user
3 | slug: /develop/api-reference/user/st.user
4 | description: st.user returns information about the logged-in user of private apps on Streamlit Community Cloud.
5 | ---
6 |
7 |
8 |
9 | ### Community Cloud
10 |
11 | On Community Cloud, if your app is not configured for authentication, st.user
will have a single attribute: email
. If a user is logged in and a member of your app's workspace, this will return the user's email. For all other cases, it returns None
.
12 |
13 | On Community Cloud, if your app is configured for authentication ([auth]
exists in your app's secrets), st.user
will behave the same as a locally running app. Remember to update your identity provider's configuration and your app's secrets to allow your new domain. A list of IP addresses used by Community Cloud is available if needed. An authentication-configured app counts as your one, allowed private app.
14 |
15 |
16 |
1 | ---
2 | title: Input widgets
3 | slug: /develop/api-reference/widgets
4 | ---
5 |
6 | # Input widgets
7 |
8 | With widgets, Streamlit allows you to bake interactivity directly into your apps with buttons, sliders, text inputs, and more.
9 |
10 | ## Button elements
11 |
12 |
13 |
14 |
15 |
16 |
17 |
python 22 | clicked = st.button("Click me") 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Display a download button widget.
34 |
35 | python 36 | st.download_button("Download file", file) 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Display a form submit button. For use with st.form
.
48 |
49 | python 50 | st.form_submit_button("Sign up") 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Display a link button.
62 |
63 | python 64 | st.link_button("Go to gallery", url) 65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Display a link to another page in a multipage app.
76 |
77 | python 78 | st.page_link("app.py", label="Home", icon="🏠") 79 | st.page_link("pages/profile.py", label="My profile") 80 |
81 |
82 |
83 |
84 |
85 |
86 | ## Selection elements
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | Display a checkbox widget.
97 |
98 | python 99 | selected = st.checkbox("I agree") 100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | Display a color picker widget.
110 |
111 | python 112 | color = st.color_picker("Pick a color") 113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | Display a rating or sentiment button group.
123 |
124 | python 125 | st.feedback("stars") 126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | Display a multiselect widget. The multiselect widget starts as empty.
136 |
137 | python 138 | choices = st.multiselect("Buy", ["milk", "apples", "potatoes"]) 139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | Display a pill-button selection widget.
149 |
150 | python 151 | st.pills("Tags", ["Sports", "AI", "Politics"]) 152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | Display a radio button widget.
162 |
163 | python 164 | choice = st.radio("Pick one", ["cats", "dogs"]) 165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | Display a segmented-button selection widget.
175 |
176 | python 177 | st.segmented_control("Filter", ["Open", "Closed", "All"]) 178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 | Display a slider widget to select items from a list.
188 |
189 | python 190 | size = st.select_slider("Pick a size", ["S", "M", "L"]) 191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 | Display a select widget.
201 |
202 | python 203 | choice = st.selectbox("Pick one", ["cats", "dogs"]) 204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | Display a toggle widget.
214 |
215 | python 216 | activated = st.toggle("Activate") 217 |
218 |
219 |
220 |
221 |
222 |
223 | ## Numeric input elements
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | Display a numeric input widget.
233 |
234 | python 235 | choice = st.number_input("Pick a number", 0, 10) 236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | Display a slider widget.
246 |
247 | python 248 | number = st.slider("Pick a number", 0, 100) 249 |
250 |
251 |
252 |
253 |
254 |
255 | ## Date and time input elements
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | Display a date input widget.
266 |
267 | python 268 | date = st.date_input("Your birthday") 269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 | Display a time input widget.
279 |
280 | python 281 | time = st.time_input("Meeting time") 282 |
283 |
284 |
285 |
286 |
287 |
288 | ## Text input elements
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 | Display a single-line text input widget.
299 |
300 | python 301 | name = st.text_input("First name") 302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 | Display a multi-line text input widget.
312 |
313 | python 314 | text = st.text_area("Text to translate") 315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 | Display a chat input widget.
325 |
326 | python 327 | prompt = st.chat_input("Say something") 328 | if prompt: 329 | st.write(f"The user has sent: {prompt}") 330 |
331 |
332 |
333 |
334 |
335 |
336 | ## Other input elements
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 | Display a widget that allows users to record with their microphone.
346 |
347 | python 348 | speech = st.audio_input("Record a voice message") 349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 | Display a data editor widget.
359 |
360 | python 361 | edited = st.data_editor(df, num_rows="dynamic") 362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 | Display a file uploader widget.
372 |
373 | python 374 | data = st.file_uploader("Upload a CSV") 375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 | Display a widget that allows users to upload images directly from a camera.
385 |
386 | python 387 | image = st.camera_input("Take a picture") 388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 | Create a draggable and resizable dashboard in Streamlit. Created by @okls.
402 |
403 | python 404 | from streamlit_elements import elements, mui, html 405 | 406 | with elements("new_element"): 407 | mui.Typography("Hello world") 408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 | Add tags to your Streamlit apps. Created by @gagan3012.
419 |
420 | python 421 | from streamlit_tags import st_tags 422 | 423 | st_tags(label='# Enter Keywords:', text='Press enter to add more', value=['Zero', 'One', 'Two'], 424 | suggestions=['five', 'six', 'seven', 'eight', 'nine', 'three', 'eleven', 'ten', 'four'], maxtags = 4, key='1') 425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 | The simplest way to handle a progress bar in streamlit app. Created by @Wirg.
436 |
437 | python 438 | from stqdm import stqdm 439 | 440 | for _ in stqdm(range(50)): 441 | sleep(0.5) 442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 | Display a Timeline in Streamlit apps using TimelineJS. Created by @innerdoc.
453 |
454 | python 455 | from streamlit_timeline import timeline 456 | 457 | with open('example.json', "r") as f: 458 | timeline(f.read(), height=800) 459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 | Alternative for st.camera_input which returns the webcam images live. Created by @blackary.
470 |
471 | python 472 | from camera_input_live import camera_input_live 473 | 474 | image = camera_input_live() 475 | st.image(value) 476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 | Ace editor component for Streamlit. Created by @okld.
487 |
488 | python 489 | from streamlit_ace import st_ace 490 | 491 | content = st_ace() 492 | content 493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 | Streamlit Component for a Chatbot UI. Created by @AI-Yash.
504 |
505 | python 506 | from streamlit_chat import message 507 | 508 | message("My message") 509 | message("Hello bot!", is_user=True) # align's the message to the right 510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 | Select a single item from a list of options in a menu. Created by @victoryhb.
521 |
522 | python 523 | from streamlit_option_menu import option_menu 524 | 525 | option_menu("Main Menu", ["Home", 'Settings'], 526 | icons=['house', 'gear'], menu_icon="cast", default_index=1) 527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 | A library with useful Streamlit extras. Created by @arnaudmiribel.
538 |
539 | python 540 | from streamlit_extras.stoggle import stoggle 541 | 542 | stoggle( 543 | "Click me!", """🥷 Surprise! Here's some additional content""",) 544 |
545 |
546 |
547 |
548 |
549 |
1 | --- 2 | title: st.audio_input 3 | slug: /develop/api-reference/widgets/st.audio_input 4 | description: st.audio_input displays a widget to upload audio from a microphone 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.button
3 | slug: /develop/api-reference/widgets/st.button
4 | description: st.button displays a button widget.
5 | keywords: button
6 | ---
7 |
8 |
9 |
10 | ### Advanced functionality
11 |
12 | Although a button is the simplest of input widgets, it's very common for buttons to be deeply tied to the use of st.session_state
. Check out our advanced guide on Button behavior and examples.
13 |
14 | ### Featured videos
15 |
16 | Check out our video on how to use one of Streamlit's core functions, the button!
17 |
18 |
19 |
20 | In the video below, we'll take it a step further and learn how to combine a button, checkbox and radio button!
21 |
22 |
23 |
1 | ---
2 | title: st.camera_input
3 | slug: /develop/api-reference/widgets/st.camera_input
4 | description: st.camera_input displays a widget to upload images from a camera
5 | ---
6 |
7 |
8 |
9 | To read the image file buffer as bytes, you can use getvalue()
on the UploadedFile
object.
10 |
11 | python 12 | import streamlit as st 13 | 14 | img_file_buffer = st.camera_input("Take a picture") 15 | 16 | if img_file_buffer is not None: 17 | # To read image file buffer as bytes: 18 | bytes_data = img_file_buffer.getvalue() 19 | # Check the type of bytes_data: 20 | # Should output: <class 'bytes'> 21 | st.write(type(bytes_data)) 22 |
23 |
24 |
25 |
26 | st.camera_input
returns an object of the UploadedFile
class, which a subclass of BytesIO. Therefore it is a "file-like" object. This means you can pass it anywhere where a file is expected, similar to st.file_uploader
.
27 |
28 |
29 |
30 | ## Image processing examples
31 |
32 | You can use the output of st.camera_input
for various downstream tasks, including image processing. Below, we demonstrate how to use the st.camera_input
widget with popular image and data processing libraries such as Pillow, NumPy, OpenCV, TensorFlow, torchvision, and PyTorch.
33 |
34 | While we provide examples for the most popular use-cases and libraries, you are welcome to adapt these examples to your own needs and favorite libraries.
35 |
36 | ### Pillow (PIL) and NumPy
37 |
38 | Ensure you have installed Pillow and NumPy.
39 |
40 | To read the image file buffer as a PIL Image and convert it to a NumPy array:
41 |
42 | python 43 | import streamlit as st 44 | from PIL import Image 45 | import numpy as np 46 | 47 | img_file_buffer = st.camera_input("Take a picture") 48 | 49 | if img_file_buffer is not None: 50 | # To read image file buffer as a PIL Image: 51 | img = Image.open(img_file_buffer) 52 | 53 | # To convert PIL Image to numpy array: 54 | img_array = np.array(img) 55 | 56 | # Check the type of img_array: 57 | # Should output: <class 'numpy.ndarray'> 58 | st.write(type(img_array)) 59 | 60 | # Check the shape of img_array: 61 | # Should output shape: (height, width, channels) 62 | st.write(img_array.shape) 63 |
64 |
65 | ### OpenCV (cv2)
66 |
67 | Ensure you have installed OpenCV and NumPy.
68 |
69 | To read the image file buffer with OpenCV:
70 |
71 | python 72 | import streamlit as st 73 | import cv2 74 | import numpy as np 75 | 76 | img_file_buffer = st.camera_input("Take a picture") 77 | 78 | if img_file_buffer is not None: 79 | # To read image file buffer with OpenCV: 80 | bytes_data = img_file_buffer.getvalue() 81 | cv2_img = cv2.imdecode(np.frombuffer(bytes_data, np.uint8), cv2.IMREAD_COLOR) 82 | 83 | # Check the type of cv2_img: 84 | # Should output: <class 'numpy.ndarray'> 85 | st.write(type(cv2_img)) 86 | 87 | # Check the shape of cv2_img: 88 | # Should output shape: (height, width, channels) 89 | st.write(cv2_img.shape) 90 |
91 |
92 | ### TensorFlow
93 |
94 | Ensure you have installed TensorFlow.
95 |
96 | To read the image file buffer as a 3 dimensional uint8 tensor with TensorFlow:
97 |
98 | python 99 | import streamlit as st 100 | import tensorflow as tf 101 | 102 | img_file_buffer = st.camera_input("Take a picture") 103 | 104 | if img_file_buffer is not None: 105 | # To read image file buffer as a 3D uint8 tensor with TensorFlow: 106 | bytes_data = img_file_buffer.getvalue() 107 | img_tensor = tf.io.decode_image(bytes_data, channels=3) 108 | 109 | # Check the type of img_tensor: 110 | # Should output: <class 'tensorflow.python.framework.ops.EagerTensor'> 111 | st.write(type(img_tensor)) 112 | 113 | # Check the shape of img_tensor: 114 | # Should output shape: (height, width, channels) 115 | st.write(img_tensor.shape) 116 |
117 |
118 | ### Torchvision
119 |
120 | Ensure you have installed Torchvision (it is not bundled with PyTorch) and PyTorch.
121 |
122 | To read the image file buffer as a 3 dimensional uint8 tensor with torchvision.io
:
123 |
124 | python 125 | import streamlit as st 126 | import torch 127 | import torchvision 128 | 129 | img_file_buffer = st.camera_input("Take a picture") 130 | 131 | if img_file_buffer is not None: 132 | # To read image file buffer as a 3D uint8 tensor with `torchvision.io`: 133 | bytes_data = img_file_buffer.getvalue() 134 | torch_img = torchvision.io.decode_image( 135 | torch.frombuffer(bytes_data, dtype=torch.uint8) 136 | ) 137 | 138 | # Check the type of torch_img: 139 | # Should output: <class 'torch.Tensor'> 140 | st.write(type(torch_img)) 141 | 142 | # Check the shape of torch_img: 143 | # Should output shape: torch.Size([channels, height, width]) 144 | st.write(torch_img.shape) 145 |
146 |
147 | ### PyTorch
148 |
149 | Ensure you have installed PyTorch and NumPy.
150 |
151 | To read the image file buffer as a 3 dimensional uint8 tensor with PyTorch:
152 |
153 | python 154 | import streamlit as st 155 | import torch 156 | import numpy as np 157 | 158 | img_file_buffer = st.camera_input("Take a picture") 159 | 160 | if img_file_buffer is not None: 161 | # To read image file buffer as a 3D uint8 tensor with PyTorch: 162 | bytes_data = img_file_buffer.getvalue() 163 | torch_img = torch.ops.image.decode_image( 164 | torch.from_numpy(np.frombuffer(bytes_data, np.uint8)), 3 165 | ) 166 | 167 | # Check the type of torch_img: 168 | # Should output: <class 'torch.Tensor'> 169 | st.write(type(torch_img)) 170 | 171 | # Check the shape of torch_img: 172 | # Should output shape: torch.Size([channels, height, width]) 173 | st.write(torch_img.shape) 174 |
175 |
1 | --- 2 | title: st.checkbox 3 | slug: /develop/api-reference/widgets/st.checkbox 4 | description: st.checkbox displays a checkbox widget. 5 | --- 6 | 7 | 8 | 9 | ### Featured videos 10 | 11 | Check out our video on how to use one of Streamlit's core functions, the checkbox! ☑ 12 | 13 | 14 | 15 | In the video below, we'll take it a step further and learn how to combine a button, checkbox and radio button! 16 | 17 | 18 |
1 | --- 2 | title: st.color_picker 3 | slug: /develop/api-reference/widgets/st.color_picker 4 | description: st.color_picker displays a color picker widget. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.date_input 3 | slug: /develop/api-reference/widgets/st.date_input 4 | description: st.date_input displays a date input widget. 5 | keywords: calendar 6 | --- 7 | 8 | 9 |
1 | --- 2 | title: st.download_button 3 | slug: /develop/api-reference/widgets/st.download_button 4 | description: st.download_button displays a download button widget. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.feedback 3 | slug: /develop/api-reference/widgets/st.feedback 4 | description: Collect user feedback or ratings with st.feedback 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.file_uploader 3 | slug: /develop/api-reference/widgets/st.file_uploader 4 | description: st.file_uploader displays a file uploader widget. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.link_button 3 | slug: /develop/api-reference/widgets/st.link_button 4 | --- 5 | 6 | 7 |
1 | --- 2 | title: st.multiselect 3 | slug: /develop/api-reference/widgets/st.multiselect 4 | description: st.multiselect displays a multiselect widget. The multiselect widget starts as empty. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.number_input 3 | slug: /develop/api-reference/widgets/st.number_input 4 | description: st.number_input displays a numeric input widget. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.page_link
3 | slug: /develop/api-reference/widgets/st.page_link
4 | description: st.page_link displays a link to another page in a multipage app or to an external page.
5 | ---
6 |
7 |
8 |
9 | Check out our tutorial to learn about building custom, dynamic menus with st.page_link
.
10 |
11 |
12 |
13 |
14 |
1 | --- 2 | title: st.pills 3 | slug: /develop/api-reference/widgets/st.pills 4 | description: st.pills displays a select widget where options display as pill buttons. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.radio
3 | slug: /develop/api-reference/widgets/st.radio
4 | description: st.radio displays a radio button widget.
5 | ---
6 |
7 |
8 |
9 |
10 |
11 | Widgets can customize how to hide their labels with the label_visibility
parameter. If "hidden", the label doesn’t show but there is still empty space for it above the widget (equivalent to label=""
). If "collapsed", both the label and the space are removed. Default is "visible". Radio buttons can also be disabled with the disabled
parameter, and oriented horizontally with the horizontal
parameter:
12 |
13 | python 14 | import streamlit as st 15 | 16 | # Store the initial value of widgets in session state 17 | if "visibility" not in st.session_state: 18 | st.session_state.visibility = "visible" 19 | st.session_state.disabled = False 20 | st.session_state.horizontal = False 21 | 22 | col1, col2 = st.columns(2) 23 | 24 | with col1: 25 | st.checkbox("Disable radio widget", key="disabled") 26 | st.checkbox("Orient radio options horizontally", key="horizontal") 27 | 28 | with col2: 29 | st.radio( 30 | "Set label visibility 👇", 31 | ["visible", "hidden", "collapsed"], 32 | key="visibility", 33 | label_visibility=st.session_state.visibility, 34 | disabled=st.session_state.disabled, 35 | horizontal=st.session_state.horizontal, 36 | ) 37 |
38 |
39 |
40 |
41 | ### Featured videos
42 |
43 | Check out our video on how to use one of Streamlit's core functions, the radio button! 🔘
44 |
45 |
46 |
47 | In the video below, we'll take it a step further and learn how to combine a button, checkbox and radio button!
48 |
49 |
50 |
1 | --- 2 | title: st.segmented_control 3 | slug: /develop/api-reference/widgets/st.segmented_control 4 | description: st.segmented_control displays a select widget where options display in a segmented button. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.select_slider 3 | slug: /develop/api-reference/widgets/st.select_slider 4 | description: st.select_slider displays a slider widget to select items from a list. 5 | --- 6 | 7 | 8 | 9 | ### Featured videos 10 | 11 | Check out our video on how to use one of Streamlit's core functions, the select slider! 🎈 12 | 13 | 14 | In the video below, we'll take it a step further and make a double-ended slider. 15 | 16 |
1 | ---
2 | title: st.selectbox
3 | slug: /develop/api-reference/widgets/st.selectbox
4 | description: st.selectbox displays a select widget.
5 | ---
6 |
7 |
8 |
9 |
10 |
11 | Select widgets can customize how to hide their labels with the label_visibility
parameter. If "hidden", the label doesn’t show but there is still empty space for it above the widget (equivalent to label=""
). If "collapsed", both the label and the space are removed. Default is "visible". Select widgets can also be disabled with the disabled
parameter:
12 |
13 | python 14 | import streamlit as st 15 | 16 | # Store the initial value of widgets in session state 17 | if "visibility" not in st.session_state: 18 | st.session_state.visibility = "visible" 19 | st.session_state.disabled = False 20 | 21 | col1, col2 = st.columns(2) 22 | 23 | with col1: 24 | st.checkbox("Disable selectbox widget", key="disabled") 25 | st.radio( 26 | "Set selectbox label visibility 👉", 27 | key="visibility", 28 | options=["visible", "hidden", "collapsed"], 29 | ) 30 | 31 | with col2: 32 | option = st.selectbox( 33 | "How would you like to be contacted?", 34 | ("Email", "Home phone", "Mobile phone"), 35 | label_visibility=st.session_state.visibility, 36 | disabled=st.session_state.disabled, 37 | ) 38 |
39 |
40 |
41 |
1 | --- 2 | title: st.slider 3 | slug: /develop/api-reference/widgets/st.slider 4 | description: st.slider displays a slider widget. 5 | --- 6 | 7 | 8 | 9 | ### Featured videos 10 | 11 | Check out our video on how to use one of Streamlit's core functions, the slider! 12 | 13 | 14 | In the video below, we'll take it a step further and make a double-ended slider. 15 | 16 |
1 | --- 2 | title: st.text_area 3 | slug: /develop/api-reference/widgets/st.text_area 4 | description: st.text_area displays a multi-line text input widget. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.text_input
3 | slug: /develop/api-reference/widgets/st.text_input
4 | description: st.text_input displays a single-line text input widget.
5 | ---
6 |
7 |
8 |
9 |
10 |
11 | Text input widgets can customize how to hide their labels with the label_visibility
parameter. If "hidden", the label doesn’t show but there is still empty space for it above the widget (equivalent to label=""
). If "collapsed", both the label and the space are removed. Default is "visible". Text input widgets can also be disabled with the disabled
parameter, and can display an optional placeholder text when the text input is empty using the placeholder
parameter:
12 |
13 | python 14 | import streamlit as st 15 | 16 | # Store the initial value of widgets in session state 17 | if "visibility" not in st.session_state: 18 | st.session_state.visibility = "visible" 19 | st.session_state.disabled = False 20 | 21 | col1, col2 = st.columns(2) 22 | 23 | with col1: 24 | st.checkbox("Disable text input widget", key="disabled") 25 | st.radio( 26 | "Set text input label visibility 👉", 27 | key="visibility", 28 | options=["visible", "hidden", "collapsed"], 29 | ) 30 | st.text_input( 31 | "Placeholder for the other text input widget", 32 | "This is a placeholder", 33 | key="placeholder", 34 | ) 35 | 36 | with col2: 37 | text_input = st.text_input( 38 | "Enter some text 👇", 39 | label_visibility=st.session_state.visibility, 40 | disabled=st.session_state.disabled, 41 | placeholder=st.session_state.placeholder, 42 | ) 43 | 44 | if text_input: 45 | st.write("You entered: ", text_input) 46 |
47 |
48 |
49 |
1 | --- 2 | title: st.time_input 3 | slug: /develop/api-reference/widgets/st.time_input 4 | description: st.time_input displays a time input widget. 5 | --- 6 | 7 | 8 |
1 | --- 2 | title: st.toggle 3 | slug: /develop/api-reference/widgets/st.toggle 4 | description: st.toggle displays a toggle widget. 5 | --- 6 | 7 | 8 |
1 | ---
2 | title: st.write and magic commands
3 | slug: /develop/api-reference/write-magic
4 | ---
5 |
6 | # st.write and magic commands
7 |
8 | Streamlit has two easy ways to display information into your app, which should typically be the
9 | first thing you try: st.write
and magic.
10 |
11 |
12 |
13 |
14 |
python 19 | st.write("Hello **world**!") 20 | st.write(my_data_frame) 21 | st.write(my_mpl_figure) 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Write generators or streams to the app with a typewriter effect.
30 |
31 | python 32 | st.write_stream(my_generator) 33 | st.write_stream(my_llm_stream) 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Any time Streamlit sees either a variable or literal value on its own line, it automatically writes that to your app using st.write
42 |
43 | python 44 | "Hello **world**!" 45 | my_data_frame 46 | my_mpl_figure 47 |
48 |
49 |
50 |
51 |
1 | ---
2 | title: Magic
3 | slug: /develop/api-reference/write-magic/magic
4 | ---
5 |
6 | ## Magic
7 |
8 | Magic commands are a feature in Streamlit that allows you to write almost anything (markdown, data,
9 | charts) without having to type an explicit command at all. Just put the thing you want to show on
10 | its own line of code, and it will appear in your app. Here's an example:
11 |
12 | python 13 | # Draw a title and some text to the app: 14 | ''' 15 | # This is the document title 16 | 17 | This is some _markdown_. 18 | ''' 19 | 20 | import pandas as pd 21 | df = pd.DataFrame({'col1': [1,2,3]}) 22 | df # 👈 Draw the dataframe 23 | 24 | x = 10 25 | 'x', x # 👈 Draw the string 'x' and then the value of x 26 | 27 | # Also works with most supported chart types 28 | import matplotlib.pyplot as plt 29 | import numpy as np 30 | 31 | arr = np.random.normal(1, 1, size=100) 32 | fig, ax = plt.subplots() 33 | ax.hist(arr, bins=20) 34 | 35 | fig # 👈 Draw a Matplotlib chart 36 |
37 |
38 | ### How Magic works
39 |
40 | Any time Streamlit sees either a variable or literal
41 | value on its own line, it automatically writes that to your app using
42 | st.write
(which you'll learn about later).
43 |
44 | Also, magic is smart enough to ignore docstrings. That is, it ignores the
45 | strings at the top of files and functions.
46 |
47 | If you prefer to call Streamlit commands more explicitly, you can always turn
48 | magic off in your ~/.streamlit/config.toml
with the following setting:
49 |
50 | toml 51 | [runner] 52 | magicEnabled = false 53 |
54 |
55 |
56 |
Right now, Magic only works in the main Python app file, not in imported files. See GitHub issue #288 for a discussion of the issues.
57 | 58 | 59 | ### Featured video 60 | 61 | Learn what thest.write
and magic commands are and how to use them.
62 |
63 |
64 |
1 | ---
2 | title: st.write
3 | slug: /develop/api-reference/write-magic/st.write
4 | description: st.write writes arguments to the app.
5 | ---
6 |
7 |
8 |
9 | ### Featured video
10 |
11 | Learn what the st.write
and magic commands are and how to use them.
12 |
13 |
14 |
1 | ---
2 | title: st.write_stream
3 | slug: /develop/api-reference/write-magic/st.write_stream
4 | description: st.write_stream writes arguments to the app using a typewriter effect.
5 | ---
6 |
7 |
8 |
9 |
10 |
11 | If your stream object is not compatible with st.write_stream
, define a wrapper around your stream object to create a compatible generator function.
12 |
13 | python 14 | for chunk in unsupported_stream: 15 | yield preprocess(chunk) 16 |
17 |
18 | For an example, see how we use Replicate with Snowflake Arctic in this code.
19 |
20 |
21 |
1 | --- 2 | title: Development concepts 3 | slug: /develop/concepts 4 | --- 5 | 6 | # Development concepts 7 | 8 | This section gives you background on how different parts of Streamlit work. 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | Streamlit's execution model makes it easy to turn your scripts into beautiful, interactive web apps. 17 | 18 | - Understand how to run your app. 19 | - Understand Streamlit's execution and client-server model. 20 | - Understand the primary tools to work with Streamlit reruns. 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Streamlit provides an automated way to build multipage apps through directory structure. 29 | 30 | - Learn how to structure and configure your multipage app. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Bring together Streamlit's architecture and execution model to design your app. Work with Streamlit commands to render dynamic and 39 | interactic content for your users. 40 | 41 | - Learn how to make your apps performant and easy-to-manage. 42 | - Learn how to structure and design your project. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | - Learn how to manage connections and secrets with Streamlit's convenient, built-in features. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Custom components extend Streamlit's functionality. 59 | 60 | - Learn how to build your own custom component. 61 | - Learn how install a third-party component. 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | Streamlit provides a variety options to customize and configure your app. 70 | 71 | - Learn how to work with configuration options, including server settings, client settings, and theming. 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | Streamlit app testing enables developers to build and run automated tests. Bring your favorite test automation software and enjoy simple syntax to simulate user input and inspect rendered output. 80 | 81 | 82 | 83 |1 | --- 2 | title: App design concepts and considerations 3 | slug: /develop/concepts/design 4 | --- 5 | 6 | # App design concepts and considerations 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | Understand how to create dynamic, animated content or update elements without rerunning your app. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Understand how buttons work with explanations and examples to avoid common mistakes. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Dataframes are a great way to display and edit data in a tabular format. Understand the UI and options available in Streamlit. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Understand the impact of defining your own Python classes within Streamlit's rerun model. 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Understand how to use multithreading within Streamlit apps. 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Understand how to localize time to your users. 55 | 56 | 57 | 58 | 59 |1 | ---
2 | title: Animate and update elements
3 | slug: /develop/concepts/design/animate
4 | description: st.add_rows appends a dataframe to the bottom of the current one in certain elements, for optimized data updates.
5 | ---
6 |
7 | # Animate and update elements
8 |
9 | Sometimes you display a chart or dataframe and want to modify it live as the app
10 | runs (for example, in a loop). Some elements have built-in methods to allow you
11 | to update them in-place without rerunning the app.
12 |
13 | Updatable elements include the following:
14 |
15 | - st.empty
containers can be written to in sequence and will always show the last thing written. They can also be cleared with an
16 | additional .empty()
called like a method.
17 | - st.dataframe
, st.table
, and many chart elements can be updated with the .add_rows()
method which appends data.
18 | - st.progress
elements can be updated with additional .progress()
calls. They can also be cleared with a .empty()
method call.
19 | - st.status
containers have an .update()
method to change their labels, expanded state, and status.
20 | - st.toast
messages can be updated in place with additional .toast()
calls.
21 |
22 | ## st.empty
containers
23 |
24 | st.empty
can hold a single element. When you write any element to an st.empty
container, Streamlit discards its previous content
25 | displays the new element. You can also st.empty
containers by calling .empty()
as a method. If you want to update a set of elements, use
26 | a plain container (st.container()
) inside st.empty
and write contents to the plain container. Rewrite the plain container and its
27 | contents as often as desired to update your app's display.
28 |
29 | ## The .add_rows()
method
30 |
31 | st.dataframe
, st.table
, and all chart functions can be mutated using the .add_rows()
method on their output. In the following example, we use my_data_element = st.line_chart(df)
. You can try the example with st.table
, st.dataframe
, and most of the other simple charts by just swapping out st.line_chart
. Note that st.dataframe
only shows the first ten rows by default and enables scrolling for additional rows. This means adding rows is not as visually apparent as it is with st.table
or the chart elements.
32 |
33 | python 34 | import streamlit as st 35 | import pandas as pd 36 | import numpy as np 37 | import time 38 | 39 | df = pd.DataFrame(np.random.randn(15, 3), columns=(["A", "B", "C"])) 40 | my_data_element = st.line_chart(df) 41 | 42 | for tick in range(10): 43 | time.sleep(.5) 44 | add_df = pd.DataFrame(np.random.randn(1, 3), columns=(["A", "B", "C"])) 45 | my_data_element.add_rows(add_df) 46 | 47 | st.button("Regenerate") 48 |
49 |
1 | ---
2 | title: Button behavior and examples
3 | slug: /develop/concepts/design/buttons
4 | ---
5 |
6 | # Button behavior and examples
7 |
8 | ## Summary
9 |
10 | Buttons created with st.button
do not retain state. They return True
on the script rerun resulting from their click and immediately return to False
on the next script rerun. If a displayed element is nested inside if st.button('Click me'):
, the element will be visible when the button is clicked and disappear as soon as the user takes their next action. This is because the script reruns and the button return value becomes False
.
11 |
12 | In this guide, we will illustrate the use of buttons and explain common misconceptions. Read on to see a variety of examples that expand on st.button
using st.session_state
. Anti-patterns are included at the end. Go ahead and pull up your favorite code editor so you can streamlit run
the examples as you read. Check out Streamlit's Basic concepts if you haven't run your own Streamlit scripts yet.
13 |
14 | ## When to use if st.button()
15 |
16 | When code is conditioned on a button's value, it will execute once in response to the button being clicked and not again (until the button is clicked again).
17 |
18 | Good to nest inside buttons:
19 |
20 | - Transient messages that immediately disappear.
21 | - Once-per-click processes that saves data to session state, a file, or
22 | a database.
23 |
24 | Bad to nest inside buttons:
25 |
26 | - Displayed items that should persist as the user continues.
27 | - Other widgets which cause the script to rerun when used.
28 | - Processes that neither modify session state nor write to a file/database.*
29 |
30 | * This can be appropriate when disposable results are desired. If you
31 | have a "Validate" button, that could be a process conditioned directly on a
32 | button. It could be used to create an alert to say 'Valid' or 'Invalid' with no
33 | need to keep that info.
34 |
35 | ## Common logic with buttons
36 |
37 | ### Show a temporary message with a button
38 |
39 | If you want to give the user a quick button to check if an entry is valid, but not keep that check displayed as the user continues.
40 |
41 | In this example, a user can click a button to check if their animal
string is in the animal_shelter
list. When the user clicks "Check availability" they will see "We have that animal!" or "We don't have that animal." If they change the animal in st.text_input
, the script reruns and the message disappears until they click "Check availability" again.
42 |
43 | python 44 | import streamlit as st 45 | 46 | animal_shelter = ['cat', 'dog', 'rabbit', 'bird'] 47 | 48 | animal = st.text_input('Type an animal') 49 | 50 | if st.button('Check availability'): 51 | have_it = animal.lower() in animal_shelter 52 | 'We have that animal!' if have_it else 'We don\'t have that animal.' 53 |
54 |
55 | Note: The above example uses magic to render the message on the frontend.
56 |
57 | ### Stateful button
58 |
59 | If you want a clicked button to continue to be True
, create a value in st.session_state
and use the button to set that value to True
in a callback.
60 |
61 | python 62 | import streamlit as st 63 | 64 | if 'clicked' not in st.session_state: 65 | st.session_state.clicked = False 66 | 67 | def click_button(): 68 | st.session_state.clicked = True 69 | 70 | st.button('Click me', on_click=click_button) 71 | 72 | if st.session_state.clicked: 73 | # The message and nested widget will remain on the page 74 | st.write('Button clicked!') 75 | st.slider('Select a value') 76 |
77 |
78 | ### Toggle button
79 |
80 | If you want a button to work like a toggle switch, consider using st.checkbox
. Otherwise, you can use a button with a callback function to reverse a boolean value saved in st.session_state
.
81 |
82 | In this example, we use st.button
to toggle another widget on and off. By displaying st.slider
conditionally on a value in st.session_state
, the user can interact with the slider without it disappearing.
83 |
84 | python 85 | import streamlit as st 86 | 87 | if 'button' not in st.session_state: 88 | st.session_state.button = False 89 | 90 | def click_button(): 91 | st.session_state.button = not st.session_state.button 92 | 93 | st.button('Click me', on_click=click_button) 94 | 95 | if st.session_state.button: 96 | # The message and nested widget will remain on the page 97 | st.write('Button is on!') 98 | st.slider('Select a value') 99 | else: 100 | st.write('Button is off!') 101 |
102 |
103 | Alternatively, you can use the value in st.session_state
on the slider's disabled
parameter.
104 |
105 | python 106 | import streamlit as st 107 | 108 | if 'button' not in st.session_state: 109 | st.session_state.button = False 110 | 111 | def click_button(): 112 | st.session_state.button = not st.session_state.button 113 | 114 | st.button('Click me', on_click=click_button) 115 | 116 | st.slider('Select a value', disabled=st.session_state.button) 117 |
118 |
119 | ### Buttons to continue or control stages of a process
120 |
121 | Another alternative to nesting content inside a button is to use a value in st.session_state
that designates the "step" or "stage" of a process. In this example, we have four stages in our script:
122 |
123 | 0. Before the user begins.
124 | 1. User enters their name.
125 | 2. User chooses a color.
126 | 3. User gets a thank-you message.
127 |
128 | A button at the beginning advances the stage from 0 to 1. A button at the end resets the stage from 3 to 0. The other widgets used in stage 1 and 2 have callbacks to set the stage. If you have a process with dependant steps and want to keep previous stages visible, such a callback forces a user to retrace subsequent stages if they change an earlier widget.
129 |
130 | python 131 | import streamlit as st 132 | 133 | if 'stage' not in st.session_state: 134 | st.session_state.stage = 0 135 | 136 | def set_state(i): 137 | st.session_state.stage = i 138 | 139 | if st.session_state.stage == 0: 140 | st.button('Begin', on_click=set_state, args=[1]) 141 | 142 | if st.session_state.stage >= 1: 143 | name = st.text_input('Name', on_change=set_state, args=[2]) 144 | 145 | if st.session_state.stage >= 2: 146 | st.write(f'Hello {name}!') 147 | color = st.selectbox( 148 | 'Pick a Color', 149 | [None, 'red', 'orange', 'green', 'blue', 'violet'], 150 | on_change=set_state, args=[3] 151 | ) 152 | if color is None: 153 | set_state(2) 154 | 155 | if st.session_state.stage >= 3: 156 | st.write(f':{color}[Thank you!]') 157 | st.button('Start Over', on_click=set_state, args=[0]) 158 |
159 |
160 | ### Buttons to modify st.session_state
161 |
162 | If you modify st.session_state
inside of a button, you must consider where that button is within the script.
163 |
164 | #### A slight problem
165 |
166 | In this example, we access st.session_state.name
both before and after the buttons which modify it. When a button ("Jane" or "John") is clicked, the script reruns. The info displayed before the buttons lags behind the info written after the button. The data in st.session_state
before the button is not updated. When the script executes the button function, that is when the conditional code to update st.session_state
creates the change. Thus, this change is reflected after the button.
167 |
168 | python 169 | import streamlit as st 170 | import pandas as pd 171 | 172 | if 'name' not in st.session_state: 173 | st.session_state['name'] = 'John Doe' 174 | 175 | st.header(st.session_state['name']) 176 | 177 | if st.button('Jane'): 178 | st.session_state['name'] = 'Jane Doe' 179 | 180 | if st.button('John'): 181 | st.session_state['name'] = 'John Doe' 182 | 183 | st.header(st.session_state['name']) 184 |
185 |
186 | #### Logic used in a callback
187 |
188 | Callbacks are a clean way to modify st.session_state
. Callbacks are executed as a prefix to the script rerunning, so the position of the button relative to accessing data is not important.
189 |
190 | python 191 | import streamlit as st 192 | import pandas as pd 193 | 194 | if 'name' not in st.session_state: 195 | st.session_state['name'] = 'John Doe' 196 | 197 | def change_name(name): 198 | st.session_state['name'] = name 199 | 200 | st.header(st.session_state['name']) 201 | 202 | st.button('Jane', on_click=change_name, args=['Jane Doe']) 203 | st.button('John', on_click=change_name, args=['John Doe']) 204 | 205 | st.header(st.session_state['name']) 206 |
207 |
208 | #### Logic nested in a button with a rerun
209 |
210 | Although callbacks are often preferred to avoid extra reruns, our first 'John Doe'/'Jane Doe' example can be modified by adding st.rerun
instead. If you need to acces data in st.session_state
before the button that modifies it, you can include st.rerun
to rerun the script after the change has been committed. This means the script will rerun twice when a button is clicked.
211 |
212 | python 213 | import streamlit as st 214 | import pandas as pd 215 | 216 | if 'name' not in st.session_state: 217 | st.session_state['name'] = 'John Doe' 218 | 219 | st.header(st.session_state['name']) 220 | 221 | if st.button('Jane'): 222 | st.session_state['name'] = 'Jane Doe' 223 | st.rerun() 224 | 225 | if st.button('John'): 226 | st.session_state['name'] = 'John Doe' 227 | st.rerun() 228 | 229 | st.header(st.session_state['name']) 230 |
231 |
232 | ### Buttons to modify or reset other widgets
233 |
234 | When a button is used to modify or reset another widget, it is the same as the above examples to modify st.session_state
. However, an extra consideration exists: you cannot modify a key-value pair in st.session_state
if the widget with that key has already been rendered on the page for the current script run.
235 |
236 |
237 |
238 | Don't do this!
239 |
240 | python 241 | import streamlit as st 242 | 243 | st.text_input('Name', key='name') 244 | 245 | # These buttons will error because their nested code changes 246 | # a widget's state after that widget within the script. 247 | if st.button('Clear name'): 248 | st.session_state.name = '' 249 | if st.button('Streamlit!'): 250 | st.session_state.name = ('Streamlit') 251 |
252 |
253 |
254 |
255 | #### Option 1: Use a key for the button and put the logic before the widget
256 |
257 | If you assign a key to a button, you can condition code on a button's state by using its value in st.session_state
. This means that logic depending on your button can be in your script before that button. In the following example, we use the .get()
method on st.session_state
because the keys for the buttons will not exist when the script runs for the first time. The .get()
method will return False
if it can't find the key. Otherwise, it will return the value of the key.
258 |
259 | python 260 | import streamlit as st 261 | 262 | # Use the get method since the keys won't be in session_state 263 | # on the first script run 264 | if st.session_state.get('clear'): 265 | st.session_state['name'] = '' 266 | if st.session_state.get('streamlit'): 267 | st.session_state['name'] = 'Streamlit' 268 | 269 | st.text_input('Name', key='name') 270 | 271 | st.button('Clear name', key='clear') 272 | st.button('Streamlit!', key='streamlit') 273 |
274 |
275 | #### Option 2: Use a callback
276 |
277 | python 278 | import streamlit as st 279 | 280 | st.text_input('Name', key='name') 281 | 282 | def set_name(name): 283 | st.session_state.name = name 284 | 285 | st.button('Clear name', on_click=set_name, args=['']) 286 | st.button('Streamlit!', on_click=set_name, args=['Streamlit']) 287 |
288 |
289 | #### Option 3: Use containers
290 |
291 | By using st.container
you can have widgets appear in different orders in your script and frontend view (webpage).
292 |
293 | python 294 | import streamlit as st 295 | 296 | begin = st.container() 297 | 298 | if st.button('Clear name'): 299 | st.session_state.name = '' 300 | if st.button('Streamlit!'): 301 | st.session_state.name = ('Streamlit') 302 | 303 | # The widget is second in logic, but first in display 304 | begin.text_input('Name', key='name') 305 |
306 |
307 | ### Buttons to add other widgets dynamically
308 |
309 | When dynamically adding widgets to the page, make sure to use an index to keep the keys unique and avoid a DuplicateWidgetID
error. In this example, we define a function display_input_row
which renders a row of widgets. That function accepts an index
as a parameter. The widgets rendered by display_input_row
use index
within their keys so that display_input_row
can be executed multiple times on a single script rerun without repeating any widget keys.
310 |
311 | python 312 | import streamlit as st 313 | 314 | def display_input_row(index): 315 | left, middle, right = st.columns(3) 316 | left.text_input('First', key=f'first_{index}') 317 | middle.text_input('Middle', key=f'middle_{index}') 318 | right.text_input('Last', key=f'last_{index}') 319 | 320 | if 'rows' not in st.session_state: 321 | st.session_state['rows'] = 0 322 | 323 | def increase_rows(): 324 | st.session_state['rows'] += 1 325 | 326 | st.button('Add person', on_click=increase_rows) 327 | 328 | for i in range(st.session_state['rows']): 329 | display_input_row(i) 330 | 331 | # Show the results 332 | st.subheader('People') 333 | for i in range(st.session_state['rows']): 334 | st.write( 335 | f'Person {i+1}:', 336 | st.session_state[f'first_{i}'], 337 | st.session_state[f'middle_{i}'], 338 | st.session_state[f'last_{i}'] 339 | ) 340 |
341 |
342 | ### Buttons to handle expensive or file-writing processes
343 |
344 | When you have expensive processes, set them to run upon clicking a button and save the results into st.session_state
. This allows you to keep accessing the results of the process without re-executing it unnecessarily. This is especially helpful for processes that save to disk or write to a database. In this example, we have an expensive_process
that depends on two parameters: option
and add
. Functionally, add
changes the output, but option
does not—option
is there to provide a parameter
345 |
346 | python 347 | import streamlit as st 348 | import pandas as pd 349 | import time 350 | 351 | def expensive_process(option, add): 352 | with st.spinner('Processing...'): 353 | time.sleep(5) 354 | df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C':[7, 8, 9]}) + add 355 | return (df, add) 356 | 357 | cols = st.columns(2) 358 | option = cols[0].selectbox('Select a number', options=['1', '2', '3']) 359 | add = cols[1].number_input('Add a number', min_value=0, max_value=10) 360 | 361 | if 'processed' not in st.session_state: 362 | st.session_state.processed = {} 363 | 364 | # Process and save results 365 | if st.button('Process'): 366 | result = expensive_process(option, add) 367 | st.session_state.processed[option] = result 368 | st.write(f'Option {option} processed with add {add}') 369 | result[0] 370 |
371 |
372 | Astute observers may think, "This feels a little like caching." We are only saving results relative to one parameter, but the pattern could easily be expanded to save results relative to both parameters. In that sense, yes, it has some similarities to caching, but also some important differences. When you save results in st.session_state
, the results are only available to the current user in their current session. If you use st.cache_data
instead, the results are available to all users across all sessions. Furthermore, if you want to update a saved result, you have to clear all saved results for that function to do so.
373 |
374 | ## Anti-patterns
375 |
376 | Here are some simplified examples of how buttons can go wrong. Be on the lookout for these common mistakes.
377 |
378 | ### Buttons nested inside buttons
379 |
380 | python 381 | import streamlit as st 382 | 383 | if st.button('Button 1'): 384 | st.write('Button 1 was clicked') 385 | if st.button('Button 2'): 386 | # This will never be executed. 387 | st.write('Button 2 was clicked') 388 |
389 |
390 | ### Other widgets nested inside buttons
391 |
392 | python 393 | import streamlit as st 394 | 395 | if st.button('Sign up'): 396 | name = st.text_input('Name') 397 | 398 | if name: 399 | # This will never be executed. 400 | st.success(f'Welcome {name}') 401 |
402 |
403 | ### Nesting a process inside a button without saving to session state
404 |
405 | python 406 | import streamlit as st 407 | import pandas as pd 408 | 409 | file = st.file_uploader("Upload a file", type="csv") 410 | 411 | if st.button('Get data'): 412 | df = pd.read_csv(file) 413 | # This display will go away with the user's next action. 414 | st.write(df) 415 | 416 | if st.button('Save'): 417 | # This will always error. 418 | df.to_csv('data.csv') 419 |
420 |
1 | ---
2 | title: Using custom Python classes in your Streamlit app
3 | slug: /develop/concepts/design/custom-classes
4 | ---
5 |
6 | # Using custom Python classes in your Streamlit app
7 |
8 | If you are building a complex Streamlit app or working with existing code, you may have custom Python classes defined in your script. Common examples include the following:
9 |
10 | - Defining a @dataclass
to store related data within your app.
11 | - Defining an Enum
class to represent a fixed set of options or values.
12 | - Defining custom interfaces to external services or databases not covered by st.connection
.
13 |
14 | Because Streamlit reruns your script after every user interaction, custom classes may be redefined multiple times within the same Streamlit session. This may result in unwanted effects, especially with class and instance comparisons. Read on to understand this common pitfall and how to avoid it.
15 |
16 | We begin by covering some general-purpose patterns you can use for different types of custom classes, and follow with a few more technical details explaining why this matters. Finally, we go into more detail about Using Enum
classes specifically, and describe a configuration option which can make them more convenient.
17 |
18 | ## Patterns to define your custom classes
19 |
20 | ### Pattern 1: Define your class in a separate module
21 |
22 | This is the recommended, general solution. If possible, move class definitions into their own module file and import them into your app script. As long as you are not editing the files that define your app, Streamlit will not re-import those classes with each rerun. Therefore, if a class is defined in an external file and imported into your script, the class will not be redefined during the session, unless you are actively editing your app.
23 |
24 | #### Example: Move your class definition
25 |
26 | Try running the following Streamlit app where MyClass
is defined within the page's script. isinstance()
will return True
on the first script run then return False
on each rerun thereafter.
27 |
28 | python 29 | # app.py 30 | import streamlit as st 31 | 32 | # MyClass gets redefined every time app.py reruns 33 | class MyClass: 34 | def __init__(self, var1, var2): 35 | self.var1 = var1 36 | self.var2 = var2 37 | 38 | if "my_instance" not in st.session_state: 39 | st.session_state.my_instance = MyClass("foo", "bar") 40 | 41 | # Displays True on the first run then False on every rerun 42 | st.write(isinstance(st.session_state.my_instance, MyClass)) 43 | 44 | st.button("Rerun") 45 |
46 |
47 | If you move the class definition out of app.py
into another file, you can make isinstance()
consistently return True
. Consider the following file structure:
48 |
49 | 50 | myproject/ 51 | ├── my_class.py 52 | └── app.py 53 |
54 |
55 | python 56 | # my_class.py 57 | class MyClass: 58 | def __init__(self, var1, var2): 59 | self.var1 = var1 60 | self.var2 = var2 61 |
62 |
63 | python 64 | # app.py 65 | import streamlit as st 66 | from my_class import MyClass # MyClass doesn't get redefined with each rerun 67 | 68 | if "my_instance" not in st.session_state: 69 | st.session_state.my_instance = MyClass("foo", "bar") 70 | 71 | # Displays True on every rerun 72 | st.write(isinstance(st.session_state.my_instance, MyClass)) 73 | 74 | st.button("Rerun") 75 |
76 |
77 | Streamlit only reloads code in imported modules when it detects the code has changed. Thus, if you are actively editing your app code, you may need to start a new session or restart your Streamlit server to avoid an undesirable class redefinition.
78 |
79 | ### Pattern 2: Force your class to compare internal values
80 |
81 | For classes that store data (like dataclasses), you may be more interested in comparing the internally stored values rather than the class itself. If you define a custom __eq__
method, you can force comparisons to be made on the internally stored values.
82 |
83 | #### Example: Define __eq__
84 |
85 | Try running the following Streamlit app and observe how the comparison is True
on the first run then False
on every rerun thereafter.
86 |
87 | python 88 | import streamlit as st 89 | from dataclasses import dataclass 90 | 91 | @dataclass 92 | class MyDataclass: 93 | var1: int 94 | var2: float 95 | 96 | if "my_dataclass" not in st.session_state: 97 | st.session_state.my_dataclass = MyDataclass(1, 5.5) 98 | 99 | # Displays True on the first run the False on every rerun 100 | st.session_state.my_dataclass == MyDataclass(1, 5.5) 101 | 102 | st.button("Rerun") 103 |
104 |
105 | Since MyDataclass
gets redefined with each rerun, the instance stored in Session State will not be equal to any instance defined in a later script run. You can fix this by forcing a comparison of internal values as follows:
106 |
107 | python 108 | import streamlit as st 109 | from dataclasses import dataclass 110 | 111 | @dataclass 112 | class MyDataclass: 113 | var1: int 114 | var2: float 115 | 116 | def __eq__(self, other): 117 | # An instance of MyDataclass is equal to another object if the object 118 | # contains the same fields with the same values 119 | return (self.var1, self.var2) == (other.var1, other.var2) 120 | 121 | if "my_dataclass" not in st.session_state: 122 | st.session_state.my_dataclass = MyDataclass(1, 5.5) 123 | 124 | # Displays True on every rerun 125 | st.session_state.my_dataclass == MyDataclass(1, 5.5) 126 | 127 | st.button("Rerun") 128 |
129 |
130 | The default Python __eq__
implementation for a regular class or @dataclass
depends on the in-memory ID of the class or class instance. To avoid problems in Streamlit, your custom __eq__
method should not depend the type()
of self
and other
.
131 |
132 | ### Pattern 3: Store your class as serialized data
133 |
134 | Another option for classes that store data is to define serialization and deserialization methods like to_str
and from_str
for your class. You can use these to store class instance data in st.session_state
rather than storing the class instance itself. Similar to pattern 2, this is a way to force comparison of the internal data and bypass the changing in-memory IDs.
135 |
136 | #### Example: Save your class instance as a string
137 |
138 | Using the same example from pattern 2, this can be done as follows:
139 |
140 | python 141 | import streamlit as st 142 | from dataclasses import dataclass 143 | 144 | @dataclass 145 | class MyDataclass: 146 | var1: int 147 | var2: float 148 | 149 | def to_str(self): 150 | return f"{self.var1},{self.var2}" 151 | 152 | @classmethod 153 | def from_str(cls, serial_str): 154 | values = serial_str.split(",") 155 | var1 = int(values[0]) 156 | var2 = float(values[1]) 157 | return cls(var1, var2) 158 | 159 | if "my_dataclass" not in st.session_state: 160 | st.session_state.my_dataclass = MyDataclass(1, 5.5).to_str() 161 | 162 | # Displays True on every rerun 163 | MyDataclass.from_str(st.session_state.my_dataclass) == MyDataclass(1, 5.5) 164 | 165 | st.button("Rerun") 166 |
167 |
168 | ### Pattern 4: Use caching to preserve your class
169 |
170 | For classes that are used as resources (database connections, state managers, APIs), consider using the cached singleton pattern. Use @st.cache_resource
to decorate a @staticmethod
of your class to generate a single, cached instance of the class. For example:
171 |
172 | python 173 | import streamlit as st 174 | 175 | class MyResource: 176 | def __init__(self, api_url: str): 177 | self._url = api_url 178 | 179 | @st.cache_resource(ttl=300) 180 | @staticmethod 181 | def get_resource_manager(api_url: str): 182 | return MyResource(api_url) 183 | 184 | # This is cached until Session State is cleared or 5 minutes has elapsed. 185 | resource_manager = MyResource.get_resource_manager("http://example.com/api/") 186 |
187 |
188 | When you use one of Streamlit's caching decorators on a function, Streamlit doesn't use the function object to look up cached values. Instead, Streamlit's caching decorators index return values using the function's qualified name and module. So, even though Streamlit redefines MyResource
with each script run, st.cache_resource
is unaffected by this. get_resource_manager()
will return its cached value with each rerun, until the value expires.
189 |
190 | ## Understanding how Python defines and compares classes
191 |
192 | So what's really happening here? We'll consider a simple example to illustrate why this is a pitfall. Feel free to skip this section if you don't want to deal more details. You can jump ahead to learn about Using Enum
classes.
193 |
194 | ### Example: What happens when you define the same class twice?
195 |
196 | Set aside Streamlit for a moment and think about this simple Python script:
197 |
198 | python 199 | from dataclasses import dataclass 200 | 201 | @dataclass 202 | class Student: 203 | student_id: int 204 | name: str 205 | 206 | Marshall_A = Student(1, "Marshall") 207 | Marshall_B = Student(1, "Marshall") 208 | 209 | # This is True (because a dataclass will compare two of its instances by value) 210 | Marshall_A == Marshall_B 211 | 212 | # Redefine the class 213 | @dataclass 214 | class Student: 215 | student_id: int 216 | name: str 217 | 218 | Marshall_C = Student(1, "Marshall") 219 | 220 | # This is False 221 | Marshall_A == Marshall_C 222 |
223 |
224 | In this example, the dataclass Student
is defined twice. All three Marshalls have the same internal values. If you compare Marshall_A
and Marshall_B
they will be equal because they were both created from the first definition of Student
. However, if you compare Marshall_A
and Marshall_C
they will not be equal because Marshall_C
was created from the second definition of Student
. Even though both Student
dataclasses are defined exactly the same, they have different in-memory IDs and are therefore different.
225 |
226 | ### What's happening in Streamlit?
227 |
228 | In Streamlit, you probably don't have the same class written twice in your page script. However, the rerun logic of Streamlit creates the same effect. Let's use the above example for an analogy. If you define a class in one script run and save an instance in Session State, then a later rerun will redefine the class and you may end up comparing a Mashall_C
in your rerun to a Marshall_A
in Session State. Since widgets rely on Session State under the hood, this is where things can get confusing.
229 |
230 | ## How Streamlit widgets store options
231 |
232 | Several Streamlit UI elements, such as st.selectbox
or st.radio
, accept multiple-choice options via an options
argument. The user of your application can typically select one or more of these options. The selected value is returned by the widget function. For example:
233 |
234 | python 235 | number = st.selectbox("Pick a number, any number", options=[1, 2, 3]) 236 | # number == whatever value the user has selected from the UI. 237 |
238 |
239 | When you call a function like st.selectbox
and pass an Iterable
to options
, the Iterable
and current selection are saved into a hidden portion of Session State called the Widget Metadata.
240 |
241 | When the user of your application interacts with the st.selectbox
widget, the broswer sends the index of their selection to your Streamlit server. This index is used to determine which values from the original options
list, saved in the Widget Metadata from the previous page execution, are returned to your application.
242 |
243 | The key detail is that the value returned by st.selectbox
(or similar widget function) is from an Iterable
saved in Session State during a previous execution of the page, NOT the values passed to options
on the current execution. There are a number of architectural reasons why Streamlit is designed this way, which we won't go into here. However, this is how we end up comparing instances of different classes when we think we are comparing instances of the same class.
244 |
245 | ### A pathological example
246 |
247 | The above explanation might be a bit confusing, so here's a pathological example to illustrate the idea.
248 |
249 | python 250 | import streamlit as st 251 | from dataclasses import dataclass 252 | 253 | @dataclass 254 | class Student: 255 | student_id: int 256 | name: str 257 | 258 | Marshall_A = Student(1, "Marshall") 259 | if "B" not in st.session_state: 260 | st.session_state.B = Student(1, "Marshall") 261 | Marshall_B = st.session_state.B 262 | 263 | options = [Marshall_A,Marshall_B] 264 | selected = st.selectbox("Pick", options) 265 | 266 | # This comparison does not return expected results: 267 | selected == Marshall_A 268 | # This comparison evaluates as expected: 269 | selected == Marshall_B 270 |
271 |
272 | As a final note, we used @dataclass
in the example for this section to illustrate a point, but in fact it is possible to encounter these same problems with classes, in general. Any class which checks class identity inside of a comparison operator—such as __eq__
or __gt__
—can exhibit these issues.
273 |
274 | ## Using Enum
classes in Streamlit
275 |
276 | The Enum
class from the Python standard library is a powerful way to define custom symbolic names that can be used as options for st.multiselect
or st.selectbox
in place of str
values.
277 |
278 | For example, you might add the following to your streamlit page:
279 |
280 | python 281 | from enum import Enum 282 | import streamlit as st 283 | 284 | # class syntax 285 | class Color(Enum): 286 | RED = 1 287 | GREEN = 2 288 | BLUE = 3 289 | 290 | selected_colors = set(st.multiselect("Pick colors", options=Color)) 291 | 292 | if selected_colors == {Color.RED, Color.GREEN}: 293 | st.write("Hooray, you found the color YELLOW!") 294 |
295 |
296 | If you're using the latest version of Streamlit, this Streamlit page will work as it appears it should. When a user picks both Color.RED
and Color.GREEN
, they are shown the special message.
297 |
298 | However, if you've read the rest of this page you might notice something tricky going on. Specifically, the Enum
class Color
gets redefined every time this script is run. In Python, if you define two Enum
classes with the same class name, members, and values, the classes and their members are still considered unique from each other. This should cause the above if
condition to always evaluate to False
. In any script rerun, the Color
values returned by st.multiselect
would be of a different class than the Color
defined in that script run.
299 |
300 | If you run the snippet above with Streamlit version 1.28.0 or less, you will not be able see the special message. Thankfully, as of version 1.29.0, Streamlit introduced a configuration option to greatly simplify the problem. That's where the enabled-by-default enumCoercion
configuration option comes in.
301 |
302 | ### Understanding the enumCoercion
configuration option
303 |
304 | When enumCoercion
is enabled, Streamlit tries to recognize when you are using an element like st.multiselect
or st.selectbox
with a set of Enum
members as options.
305 |
306 | If Streamlit detects this, it will convert the widget's returned values to members of the Enum
class defined in the latest script run. This is something we call automatic Enum
coercion.
307 |
308 | This behavior is configurable via the enumCoercion
setting in your Streamlit config.toml
file. It is enabled by default, and may be disabled or set to a stricter set of matching criteria.
309 |
310 | If you find that you still encounter issues with enumCoercion
enabled, consider using the custom class patterns described above, such as moving your Enum
class definition to a separate module file.
311 |
1 | ---
2 | title: Dataframes
3 | slug: /develop/concepts/design/dataframes
4 | ---
5 |
6 | # Dataframes
7 |
8 | Dataframes are a great way to display and edit data in a tabular format. Working with Pandas DataFrames and other tabular data structures is key to data science workflows. If developers and data scientists want to display this data in Streamlit, they have multiple options: st.dataframe
and st.data_editor
. If you want to solely display data in a table-like UI, st.dataframe is the way to go. If you want to interactively edit data, use st.data_editor. We explore the use cases and advantages of each option in the following sections.
9 |
10 | ## Display dataframes with st.dataframe
11 |
12 | Streamlit can display dataframes in a table-like UI via st.dataframe
:
13 |
14 | python 15 | import streamlit as st 16 | import pandas as pd 17 | 18 | df = pd.DataFrame( 19 | [ 20 | {"command": "st.selectbox", "rating": 4, "is_widget": True}, 21 | {"command": "st.balloons", "rating": 5, "is_widget": False}, 22 | {"command": "st.time_input", "rating": 3, "is_widget": True}, 23 | ] 24 | ) 25 | 26 | st.dataframe(df, use_container_width=True) 27 |
28 |
29 |
30 |
31 | ## st.dataframe
UI features
32 |
33 | st.dataframe
provides additional functionality by using glide-data-grid under the hood:
34 |
35 | - Column sorting: To sort columns, select their headers, or select "Sort ascending" or "Sort descending" from the header menu (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>more_vert).
36 | - Column resizing: To resize columns, drag and drop column header borders, or select "Autosize" from the header menu.
37 | - Column hiding: To hide columns, select "Hide column" from the header menu.
38 | - Reorder and pin columns: To reorder columns or pin them on the left, drag and drop column headers or select "Pin column" from the header menu, respectively.
39 | - Format numbers, dates, and times: To change the format of numeric columns, select an option under "Format" in the header menu.
40 | - Dataframe resizing: To resize dataframes, drag and drop the bottom right corner.
41 | - Fullscreen view: To enlarge dataframes to fullscreen, select the fullscreen icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>fullscreen) in the toolbar.
42 | - Search: To search through the data, select the search icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>search) in the toolbar or use hotkeys (⌘+F
or Ctrl+F
).
43 | - Download: To download the data as a CSV file, select the download icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>download) in the toolbar.
44 | - Copy to clipboard: To copy the data to the clipboard, select one or multiple cells, use the hotkeys (⌘+C
or Ctrl+C
), and paste them into your favorite spreadsheet software.
45 |
46 |
47 |
48 | Try out all the UI features using the embedded app from the prior section.
49 |
50 | In addition to Pandas DataFrames, st.dataframe
also supports other common Python types, e.g., list, dict, or numpy array. It also supports Snowpark and PySpark DataFrames, which allow you to lazily evaluate and pull data from databases. This can be useful for working with large datasets.
51 |
52 | ## Edit data with st.data_editor
53 |
54 | Streamlit supports editable dataframes via the st.data_editor
command. Check out its API in st.data_editor. It shows the dataframe in a table, similar to st.dataframe
. But in contrast to st.dataframe
, this table isn't static! The user can click on cells and edit them. The edited data is then returned on the Python side. Here's an example:
55 |
56 | python 57 | df = pd.DataFrame( 58 | [ 59 | {"command": "st.selectbox", "rating": 4, "is_widget": True}, 60 | {"command": "st.balloons", "rating": 5, "is_widget": False}, 61 | {"command": "st.time_input", "rating": 3, "is_widget": True}, 62 | ] 63 | ) 64 | 65 | edited_df = st.data_editor(df) # 👈 An editable dataframe 66 | 67 | favorite_command = edited_df.loc[edited_df["rating"].idxmax()]["command"] 68 | st.markdown(f"Your favorite command is **{favorite_command}** 🎈") 69 |
70 |
71 |
72 |
73 | Try it out by double-clicking on any cell. You'll notice you can edit all cell values. Try editing the values in the rating column and observe how the text output at the bottom changes:
74 |
75 | ## st.data_editor
UI features
76 |
77 | st.data_editor
also supports a few additional things:
78 |
79 | - Add and delete rows: You can do this by setting num_rows= "dynamic"
when calling st.data_editor
. This will allow users to add and delete rows as needed.
80 | - Copy and paste support: Copy and paste both between st.data_editor
and spreadsheet software like Google Sheets and Excel.
81 | - Access edited data: Access only the individual edits instead of the entire edited data structure via Session State.
82 | - Bulk edits: Similar to Excel, just drag a handle to edit neighboring cells.
83 | - Automatic input validation: Column Configuration provides strong data type support and other configurable options. For example, there's no way to enter letters into a number cell. Number cells can have a designated min and max.
84 | - Edit common data structures: st.data_editor
supports lists, dicts, NumPy ndarray, and more!
85 |
86 |
87 |
88 | ### Add and delete rows
89 |
90 | With st.data_editor
, viewers can add or delete rows via the table UI. This mode can be activated by setting the num_rows
parameter to "dynamic"
:
91 |
92 | python 93 | edited_df = st.data_editor(df, num_rows="dynamic") 94 |
95 |
96 | - To add new rows, click the plus icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>add) in the toolbar. Alternatively, click inside a shaded cell below the bottom row of the table.
97 | - To delete rows, select one or more rows using the checkboxes on the left. Click the delete icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>delete) or press the delete
key on your keyboard.
98 |
99 |
100 |
101 | ### Copy and paste support
102 |
103 | The data editor supports pasting in tabular data from Google Sheets, Excel, Notion, and many other similar tools. You can also copy-paste data between st.data_editor
instances. This functionality, powered by the Clipboard API, can be a huge time saver for users who need to work with data across multiple platforms. To try it out:
104 |
105 | 1. Copy data from this Google Sheets document to your clipboard.
106 | 2. Single click any cell in the name
column in the app above. Paste it in using hotkeys (⌘+V
or Ctrl+V
).
107 |
108 |
109 |
110 | Every cell of the pasted data will be evaluated individually and inserted into the cells if the data is compatible with the column type. For example, pasting in non-numerical text data into a number column will be ignored.
111 |
112 |
113 |
114 |
115 |
116 | If you embed your apps with iframes, you'll need to allow the iframe to access the clipboard if you want to use the copy-paste functionality. To do so, give the iframe clipboard-write
and clipboard-read
permissions. E.g.
117 |
118 | javascript 119 | <iframe allow="clipboard-write;clipboard-read;" ... src="https://your-app-url"></iframe> 120 |
121 |
122 | As developers, ensure the app is served with a valid, trusted certificate when using TLS. If users encounter issues with copying and pasting data, direct them to check if their browser has activated clipboard access permissions for the Streamlit application, either when prompted or through the browser's site settings.
123 |
124 |
125 |
126 | ### Access edited data
127 |
128 | Sometimes, it is more convenient to know which cells have been changed rather than getting the entire edited dataframe back. Streamlit makes this easy through the use of Session State. If a key
parameter is set, Streamlit will store any changes made to the dataframe in Session State.
129 |
130 | This snippet shows how you can access changed data using Session State:
131 |
132 | python 133 | st.data_editor(df, key="my_key", num_rows="dynamic") # 👈 Set a key 134 | st.write("Here's the value in Session State:") 135 | st.write(st.session_state["my_key"]) # 👈 Show the value in Session State 136 |
137 |
138 | In this code snippet, the key
parameter is set to "my_key"
. After the data editor is created, the value associated to "my_key"
in Session State is displayed in the app using st.write
. This shows the additions, edits, and deletions that were made.
139 |
140 | This can be useful when working with large dataframes and you only need to know which cells have changed, rather than access the entire edited dataframe.
141 |
142 |
143 |
144 | Use all we've learned so far and apply them to the above embedded app. Try editing cells, adding new rows, and deleting rows.
145 |
146 | Notice how edits to the table are reflected in Session State. When you make any edits, a rerun is triggered which sends the edits to the backend. The widget's state is a JSON object containing three properties: edited_rows, added_rows, and deleted rows:.
147 |
148 |
149 |
150 | When going from st.experimental_data_editor
to st.data_editor
in 1.23.0, the data editor's representation in st.session_state
was changed. The edited_cells
dictionary is now called edited_rows
and uses a different format ({0: {"column name": "edited value"}}
instead of {"0:1": "edited value"}
). You may need to adjust your code if your app uses st.experimental_data_editor
in combination with st.session_state
."
151 |
152 |
153 |
154 | - edited_rows
is a dictionary containing all edits. Keys are zero-based row indices and values are dictionaries that map column names to edits (e.g. {0: {"col1": ..., "col2": ...}}
).
155 | - added_rows
is a list of newly added rows. Each value is a dictionary with the same format as above (e.g. [{"col1": ..., "col2": ...}]
).
156 | - deleted_rows
is a list of row numbers that have been deleted from the table (e.g. [0, 2]
).
157 |
158 | st.data_editor
does not support reordering rows, so added rows will always be appended to the end of the dataframe with any edits and deletions applicable to the original rows.
159 |
160 | ### Bulk edits
161 |
162 | The data editor includes a feature that allows for bulk editing of cells. Similar to Excel, you can drag a handle across a selection of cells to edit their values in bulk. You can even apply commonly used keyboard shortcuts in spreadsheet software. This is useful when you need to make the same change across multiple cells, rather than editing each cell individually.
163 |
164 | ### Edit common data structures
165 |
166 | Editing doesn't just work for Pandas DataFrames! You can also edit lists, tuples, sets, dictionaries, NumPy arrays, or Snowpark & PySpark DataFrames. Most data types will be returned in their original format. But some types (e.g. Snowpark and PySpark) are converted to Pandas DataFrames. To learn about all the supported types, read the st.data_editor API.
167 |
168 | For example, you can easily let the user add items to a list:
169 |
170 | python 171 | edited_list = st.data_editor(["red", "green", "blue"], num_rows= "dynamic") 172 | st.write("Here are all the colors you entered:") 173 | st.write(edited_list) 174 |
175 |
176 | Or numpy arrays:
177 |
178 | python 179 | import numpy as np 180 | 181 | st.data_editor(np.array([ 182 | ["st.text_area", "widget", 4.92], 183 | ["st.markdown", "element", 47.22] 184 | ])) 185 |
186 |
187 | Or lists of records:
188 |
189 | python 190 | st.data_editor([ 191 | {"name": "st.text_area", "type": "widget"}, 192 | {"name": "st.markdown", "type": "element"}, 193 | ]) 194 |
195 |
196 | Or dictionaries and many more types!
197 |
198 | python 199 | st.data_editor({ 200 | "st.text_area": "widget", 201 | "st.markdown": "element" 202 | }) 203 |
204 |
205 | ### Automatic input validation
206 |
207 | The data editor includes automatic input validation to help prevent errors when editing cells. For example, if you have a column that contains numerical data, the input field will automatically restrict the user to only entering numerical data. This helps to prevent errors that could occur if the user were to accidentally enter a non-numerical value. Additional input validation can be configured through the Column configuration API. Keep reading below for an overview of column configuration, including validation options.
208 |
209 | ## Configuring columns
210 |
211 | You can configure the display and editing behavior of columns in st.dataframe
and st.data_editor
via the Column configuration API. We have developed the API to let you add images, charts, and clickable URLs in dataframe and data editor columns. Additionally, you can make individual columns editable, set columns as categorical and specify which options they can take, hide the index of the dataframe, and much more.
212 |
213 | Column configuration includes the following column types: Text, Number, Checkbox, Selectbox, Date, Time, Datetime, List, Link, Image, Line chart, Bar chart, and Progress. There is also a generic Column option. See the embedded app below to view these different column types. Each column type is individually previewed in the Column configuration API documentation.
214 |
215 |
216 |
217 | ### Format values
218 |
219 | A format
parameter is available in column configuration for Text, Date, Time, and Datetime columns. Chart-like columns can also be formatted. Line chart and Bar chart columns have a y_min
and y_max
parameters to set the vertical bounds. For a Progress column, you can declare the horizontal bounds with min_value
and max_value
.
220 |
221 | ### Validate input
222 |
223 | When specifying a column configuration, you can declare not only the data type of the column but also value restrictions. All column configuration elements allow you to make a column required with the keyword parameter required=True
.
224 |
225 | For Text and Link columns, you can specify the maximum number of characters with max_chars
or use regular expressions to validate entries through validate
. Numerical columns, including Number, Date, Time, and Datetime have min_value
and max_value
parameters. Selectbox columns have a configurable list of options
.
226 |
227 | The data type for Number columns is float
by default. Passing a value of type int
to any of min_value
, max_value
, step
, or default
will set the type for the column as int
.
228 |
229 | ### Configure an empty dataframe
230 |
231 | You can use st.data_editor
to collect tabular input from a user. When starting from an empty dataframe, default column types are text. Use column configuration to specify the data types you want to collect from users.
232 |
233 | python 234 | import streamlit as st 235 | import pandas as pd 236 | 237 | df = pd.DataFrame(columns=['name','age','color']) 238 | colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] 239 | config = { 240 | 'name' : st.column_config.TextColumn('Full Name (required)', width='large', required=True), 241 | 'age' : st.column_config.NumberColumn('Age (years)', min_value=0, max_value=122), 242 | 'color' : st.column_config.SelectboxColumn('Favorite Color', options=colors) 243 | } 244 | 245 | result = st.data_editor(df, column_config = config, num_rows='dynamic') 246 | 247 | if st.button('Get results'): 248 | st.write(result) 249 |
250 |
251 |
252 |
253 | ## Additional formatting options
254 |
255 | In addition to column configuration, st.dataframe
and st.data_editor
have a few more parameters to customize the display of your dataframe.
256 |
257 | - hide_index
: Set to True
to hide the dataframe's index.
258 | - column_order
: Pass a list of column labels to specify the order of display.
259 | - disabled
: Pass a list of column labels to disable them from editing. This let's you avoid disabling them individually.
260 |
261 | ## Handling large datasets
262 |
263 | st.dataframe
and st.data_editor
have been designed to theoretically handle tables with millions of rows thanks to their highly performant implementation using the glide-data-grid library and HTML canvas. However, the maximum amount of data that an app can realistically handle will depend on several other factors, including:
264 |
265 | 1. The maximum size of WebSocket messages: Streamlit's WebSocket messages are configurable via the server.maxMessageSize
config option, which limits the amount of data that can be transferred via the WebSocket connection at once.
266 | 2. The server memory: The amount of data that your app can handle will also depend on the amount of memory available on your server. If the server's memory is exceeded, the app may become slow or unresponsive.
267 | 3. The user's browser memory: Since all the data needs to be transferred to the user's browser for rendering, the amount of memory available on the user's device can also affect the app's performance. If the browser's memory is exceeded, it may crash or become unresponsive.
268 |
269 | In addition to these factors, a slow network connection can also significantly slow down apps that handle large datasets.
270 |
271 | When handling large datasets with more than 150,000 rows, Streamlit applies additional optimizations and disables column sorting. This can help to reduce the amount of data that needs to be processed at once and improve the app's performance.
272 |
273 | ## Limitations
274 |
275 | - Streamlit casts all column names to strings internally, so st.data_editor
will return a DataFrame where all column names are strings.
276 | - The dataframe toolbar is not currently configurable.
277 | - While Streamlit's data editing capabilities offer a lot of functionality, editing is enabled for a limited set of column types (TextColumn, NumberColumn, LinkColumn, CheckboxColumn, SelectboxColumn, DateColumn, TimeColumn, and DatetimeColumn). We are actively working on supporting editing for other column types as well, such as images, lists, and charts.
278 | - Almost all editable datatypes are supported for index editing. However, pandas.CategoricalIndex
and pandas.MultiIndex
are not supported for editing.
279 | - Sorting is not supported for st.data_editor
when num_rows="dynamic"
.
280 | - Sorting is deactivated to optimize performance on large datasets with more than 150,000 rows.
281 |
282 | We are continually working to improve Streamlit's handling of DataFrame and add functionality to data editing, so keep an eye out for updates.
283 |
1 | ---
2 | title: Threading in Streamlit
3 | slug: /develop/concepts/design/multithreading
4 | ---
5 |
6 | # Multithreading in Streamlit
7 |
8 | Multithreading is a type of concurrency, which improves the efficiency of computer programs. It's a way for processors to multitask. Streamlit uses threads within its architecture, which can make it difficult for app developers to include their own multithreaded processes. Streamlit does not officially support multithreading in app code, but this guide provides information on how it can be accomplished.
9 |
10 | ## Prerequisites
11 |
12 | - You should have a basic understanding of Streamlit's architecture.
13 |
14 | ## When to use multithreading
15 |
16 | Multithreading is just one type of concurrency. Multiprocessing and coroutines are other forms of concurrency. You need to understand how your code is bottlenecked to choose the correct kind of concurrency.
17 |
18 | Multiprocessing is inherently parallel, meaning that resources are split and multiple tasks are performed simultaneously. Therefore, multiprocessing is helpful with compute-bound operations. In contrast, multithreading and coroutines are not inherently parallel and instead allow resource switching. This makes them good choices when your code is stuck waiting for something, like an IO operation. AsyncIO uses coroutines and may be preferable with very slow IO operations. Threading may be preferable with faster IO operations. For a helpful guide to using AsyncIO with Streamlit, see this Medium article by Sehmi-Conscious Thoughts.
19 |
20 | Don't forget that Streamlit has fragments and caching, too! Use caching to avoid unnecessarily repeating computations or IO operations. Use fragments to isolate a bit of code you want to update separately from the rest of the app. You can set fragments to rerun at a specified interval, so they can be used to stream updates to a chart or table.
21 |
22 | ## Threads created by Streamlit
23 |
24 | Streamlit creates two types of threads in Python:
25 |
26 | - The server thread runs the Tornado web (HTTP + WebSocket) server.
27 | - A script thread runs page code — one thread for each script run in a session.
28 |
29 | When a user connects to your app, this creates a new session and runs a script thread to initialize the app for that user. As the script thread runs, it renders elements in the user's browser tab and reports state back to the server. When the user interacts with the app, another script thread runs, re-rendering the elements in the browser tab and updating state on the server.
30 |
31 | This is a simplifed illustration to show how Streamlit works:
32 |
33 |
34 |
35 | ##
streamlit.errors.NoSessionContext
36 |
37 | Many Streamlit commands, including st.session_state
, expect to be called from a script thread. When Streamlit is running as expected, such commands use the ScriptRunContext
attached to the script thread to ensure they work within the intended session and update the correct user's view. When those Streamlit commands can't find any ScriptRunContext
, they raise a streamlit.errors.NoSessionContext
exception. Depending on your logger settings, you may also see a console message identifying a thread by name and warning, "missing ScriptRunContext!"
38 |
39 | ## Creating custom threads
40 |
41 | When you work with IO-heavy operations like remote query or data loading, you may need to mitigate delays. A general programming strategy is to create threads and let them work concurrently. However, if you do this in a Streamlit app, these custom threads may have difficulty interacting with your Streamlit server.
42 |
43 | This section introduces two patterns to let you create custom threads in your Streamlit app. These are only patterns to provide a starting point rather than complete solutions.
44 |
45 | ### Option 1: Do not use Streamlit commands within a custom thread
46 |
47 | If you don't call Streamlit commands from a custom thread, you can avoid the problem entirely. Luckily Python threading provides ways to start a thread and collect its result from another thread.
48 |
49 | In the following example, five custom threads are created from the script thread. After the threads are finished running, their results are displayed in the app.
50 |
51 | python 52 | import streamlit as st 53 | import time 54 | from threading import Thread 55 | 56 | 57 | class WorkerThread(Thread): 58 | def __init__(self, delay): 59 | super().__init__() 60 | self.delay = delay 61 | self.return_value = None 62 | 63 | def run(self): 64 | start_time = time.time() 65 | time.sleep(self.delay) 66 | end_time = time.time() 67 | self.return_value = f"start: {start_time}, end: {end_time}" 68 | 69 | 70 | delays = [5, 4, 3, 2, 1] 71 | threads = [WorkerThread(delay) for delay in delays] 72 | for thread in threads: 73 | thread.start() 74 | for thread in threads: 75 | thread.join() 76 | for i, thread in enumerate(threads): 77 | st.header(f"Thread {i}") 78 | st.write(thread.return_value) 79 | 80 | st.button("Rerun") 81 |
82 |
83 |
84 |
85 | If you want to display results in your app as various custom threads finish running, use containers. In the following example, five custom threads are created similarly to the previous example. However, five containers are initialized before running the custom threads and a while
loop is used to display results as they become available. Since the Streamlit write
command is called outside of the custom threads, this does not raise an exception.
86 |
87 | python 88 | import streamlit as st 89 | import time 90 | from threading import Thread 91 | 92 | 93 | class WorkerThread(Thread): 94 | def __init__(self, delay): 95 | super().__init__() 96 | self.delay = delay 97 | self.return_value = None 98 | 99 | def run(self): 100 | start_time = time.time() 101 | time.sleep(self.delay) 102 | end_time = time.time() 103 | self.return_value = f"start: {start_time}, end: {end_time}" 104 | 105 | 106 | delays = [5, 4, 3, 2, 1] 107 | result_containers = [] 108 | for i, delay in enumerate(delays): 109 | st.header(f"Thread {i}") 110 | result_containers.append(st.container()) 111 | 112 | threads = [WorkerThread(delay) for delay in delays] 113 | for thread in threads: 114 | thread.start() 115 | thread_lives = [True] * len(threads) 116 | 117 | while any(thread_lives): 118 | for i, thread in enumerate(threads): 119 | if thread_lives[i] and not thread.is_alive(): 120 | result_containers[i].write(thread.return_value) 121 | thread_lives[i] = False 122 | time.sleep(0.5) 123 | 124 | for thread in threads: 125 | thread.join() 126 | 127 | st.button("Rerun") 128 |
129 |
130 |
131 |
132 | ### Option 2: Expose ScriptRunContext
to the thread
133 |
134 | If you want to call Streamlit commands from within your custom threads, you must attach the correct ScriptRunContext
to the thread.
135 |
136 |
137 |
138 | - This is not officially supported and may change in a future version of Streamlit.
139 | - This may not work with all Streamlit commands.
140 | - Ensure custom threads do not outlive the script thread owning the ScriptRunContext
. Leaking of ScriptRunContext
may cause security vulnerabilities, fatal errors, or unexpected behavior.
141 |
142 |
143 |
144 | In the following example, a custom thread with ScriptRunContext
attached can call st.write
without a warning.
145 |
146 | python 147 | import streamlit as st 148 | from streamlit.runtime.scriptrunner import add_script_run_ctx, get_script_run_ctx 149 | import time 150 | from threading import Thread 151 | 152 | 153 | class WorkerThread(Thread): 154 | def __init__(self, delay, target): 155 | super().__init__() 156 | self.delay = delay 157 | self.target = target 158 | 159 | def run(self): 160 | # runs in custom thread, but can call Streamlit APIs 161 | start_time = time.time() 162 | time.sleep(self.delay) 163 | end_time = time.time() 164 | self.target.write(f"start: {start_time}, end: {end_time}") 165 | 166 | 167 | delays = [5, 4, 3, 2, 1] 168 | result_containers = [] 169 | for i, delay in enumerate(delays): 170 | st.header(f"Thread {i}") 171 | result_containers.append(st.container()) 172 | 173 | threads = [ 174 | WorkerThread(delay, container) 175 | for delay, container in zip(delays, result_containers) 176 | ] 177 | for thread in threads: 178 | add_script_run_ctx(thread, get_script_run_ctx()) 179 | thread.start() 180 | 181 | for thread in threads: 182 | thread.join() 183 | 184 | st.button("Rerun") 185 |
186 |
187 |
188 |
1 | ---
2 | title: Working with timezones
3 | slug: /develop/concepts/design/timezone-handling
4 | ---
5 |
6 | # Working with timezones
7 |
8 | In general, working with timezones can be tricky. Your Streamlit app users are not necessarily in the same timezone as the server running your app. It is especially true of public apps, where anyone in the world (in any timezone) can access your app. As such, it is crucial to understand how Streamlit handles timezones, so you can avoid unexpected behavior when displaying datetime
information.
9 |
10 | ## How Streamlit handles timezones
11 |
12 | Streamlit always shows datetime
information on the frontend with the same information as its corresponding datetime
instance in the backend. I.e., date or time information does not automatically adjust to the users' timezone. We distinguish between the following two cases:
13 |
14 | ### datetime
instance without a timezone (naive)
15 |
16 | When you provide a datetime
instance without specifying a timezone, the frontend shows the datetime
instance without timezone information. For example (this also applies to other widgets like st.dataframe
):
17 |
18 | python 19 | import streamlit as st 20 | from datetime import datetime 21 | 22 | st.write(datetime(2020, 1, 10, 10, 30)) 23 | # Outputs: 2020-01-10 10:30:00 24 |
25 |
26 | Users of the above app always see the output as 2020-01-10 10:30:00
.
27 |
28 | ### datetime
instance with a timezone
29 |
30 | When you provide a datetime
instance and specify a timezone, the frontend shows the datetime
instance in that same timezone. For example (this also applies to other widgets like st.dataframe
):
31 |
32 | python 33 | import streamlit as st 34 | from datetime import datetime 35 | import pytz 36 | 37 | st.write(datetime(2020, 1, 10, 10, 30, tzinfo=pytz.timezone("EST"))) 38 | # Outputs: 2020-01-10 10:30:00-05:00 39 |
40 |
41 | Users of the above app always see the output as 2020-01-10 10:30:00-05:00
.
42 |
43 | In both cases, neither the date nor time information automatically adjusts to the users' timezone on the frontend. What users see is identical to the corresponding datetime
instance in the backend. It is currently not possible to automatically adjust the date or time information to the timezone of the users viewing the app.
44 |
45 |
46 |
47 | The legacy version of the st.dataframe
has issues with timezones. We do not plan to roll out additional fixes or enhancements for the legacy dataframe. If you need stable timezone support, please consider switching to the arrow serialization by changing the config setting, config.dataFrameSerialization = "arrow".
48 |
49 |
50 |
1 | ---
2 | title: Streamlit's native app testing framework
3 | slug: /develop/concepts/app-testing
4 | ---
5 |
6 | # Streamlit's native app testing framework
7 |
8 | Streamlit app testing enables developers to build and run automated tests. Bring your favorite test automation software and enjoy simple syntax to simulate user input and inspect rendered output.
9 |
10 | The provided class, AppTest, simulates a running app and provides methods to set up, manipulate, and inspect the app contents via API instead of a browser UI. AppTest provides similar functionality to browser automation tools like Selenium or Playwright, but with less overhead to write and execute tests. Use our testing framework with a tool like pytest to execute or automate your tests. A typical pattern is to build a suite of tests for an app to ensure consistent functionality as the app evolves. The tests run locally and/or in a CI environment like GitHub Actions.
11 |
12 |
13 | <InlineCallout
14 | color="indigo-70"
15 | icon="science"
16 | bold="Get started"
17 | href="/develop/concepts/app-testing/get-started"
18 | >introduces you to the app testing framework and how to execute tests using pytest
. Learn how to initialize and run simulated apps, including how to retrieve, manipulate, and inspect app elements.
19 | <InlineCallout
20 | color="indigo-70"
21 | icon="password"
22 | bold="Beyond the basics"
23 | href="/develop/concepts/app-testing/beyond-the-basics"
24 | >explains how to work with secrets and Session State within app tests, including how to test multipage apps.
25 | <InlineCallout
26 | color="indigo-70"
27 | icon="play_circle"
28 | bold="Automate your tests"
29 | href="/develop/concepts/app-testing/automate-tests"
30 | >with Continuous Integration (CI) to validate app changes over time.
31 | <InlineCallout
32 | color="indigo-70"
33 | icon="quiz"
34 | bold="Example"
35 | href="/develop/concepts/app-testing/examples"
36 | >puts together the concepts explained above. Check out an app with multiple tests in place.
37 | <InlineCallout
38 | color="indigo-70"
39 | icon="saved_search"
40 | bold="Cheat sheet"
41 | href="/develop/concepts/app-testing/cheat-sheet"
42 | >is a compact reference summarizing the available syntax.
43 |
44 |
1 | ---
2 | title: Automate your tests with CI
3 | slug: /develop/concepts/app-testing/automate-tests
4 | ---
5 |
6 | # Automate your tests with CI
7 |
8 | One of the key benefits of app testing is that tests can be automated using Continuous Integration (CI). By running tests automatically during development, you can validate that changes to your app don't break existing functionality. You can verify app code as you commit, catch bugs early, and prevent accidental breaks before deployment.
9 |
10 | There are many popular CI tools, including GitHub Actions, Jenkins, GitLab CI, Azure DevOps, and Circle CI. Streamlit app testing will integrate easily with any of them similar to any other Python tests.
11 |
12 | ## GitHub Actions
13 |
14 | Since many Streamlit apps (and all Community Cloud apps) are built in GitHub, this page uses examples from GitHub Actions. For more information about GitHub Actions, see:
15 |
16 | - Quickstart for GitHub Actions
17 | - GitHub Actions: About continuous integration
18 | - GitHub Actions: Build & test Python
19 |
20 | ## Streamlit App Action
21 |
22 | Streamlit App Action provides an easy way to add automated testing to your app repository in GitHub. It also includes basic smoke testing for each page of your app without you writing any test code.
23 |
24 | To install Streamlit App Action, add a workflow .yml
file to your repository's .github/workflows/
folder. For example:
25 |
26 | yaml 27 | # .github/workflows/streamlit-app.yml 28 | name: Streamlit app 29 | 30 | on: 31 | push: 32 | branches: ["main"] 33 | pull_request: 34 | branches: ["main"] 35 | 36 | permissions: 37 | contents: read 38 | 39 | jobs: 40 | streamlit: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | - uses: actions/setup-python@v5 45 | with: 46 | python-version: "3.11" 47 | - uses: streamlit/[email protected] 48 | with: 49 | app-path: streamlit_app.py 50 |
51 |
52 | Let's take a look in more detail at what this action workflow is doing.
53 |
54 | ### Triggering the workflow
55 |
56 | yaml 57 | on: 58 | push: 59 | branches: ["main"] 60 | pull_request: 61 | branches: ["main"] 62 |
63 |
64 | This workflow will be triggered and execute tests on pull requests targeting the main
branch, as well as any new commits pushed to the main
branch. Note that it will also execute the tests on subsequent commits to any open pull requests. See GitHub Actions: Triggering a workflow for more information and examples.
65 |
66 | ### Setting up the test environment
67 |
68 | yaml 69 | jobs: 70 | streamlit: 71 | runs-on: ubuntu-latest 72 | steps: 73 | - uses: actions/checkout@v4 74 | - uses: actions/setup-python@v5 75 | with: 76 | python-version: "3.11" 77 |
78 |
79 | The workflow has a streamlit
job that executes a series of steps. The job runs on a Docker container with the ubuntu-latest
image.
80 |
81 | - actions/checkout@v4
checks out the current repository code from GitHub and copies the code to the job environment.
82 | - actions/setup-python@v5
installs Python version 3.11.
83 |
84 | ### Running the app tests
85 |
86 | yaml 87 | - uses: streamlit/[email protected] 88 | with: 89 | app-path: streamlit_app.py 90 |
91 |
92 | Streamlit App Action does the following:
93 |
94 | - Install pytest
and install any dependencies specified in requirements.txt
.
95 | - Run the built-in app smoke tests.
96 | - Run any other Python tests found in the repository.
97 |
98 |
99 |
100 | If your app doesn't include requirements.txt
in the repository root directory, you will need to add a step to install dependencies with your chosen package manager before running Streamlit App Action.
101 |
102 |
103 |
104 | The built-in smoke tests have the following behavior:
105 |
106 | - Run the app specified at app-path
as an AppTest.
107 | - Validate that it completes successfully and does not result in an uncaught exception.
108 | - Do the same for any additional pages/
of the app relative to app-path
.
109 |
110 | If you want to run Streamlit App Action without the smoke tests, you can set skip-smoke: true
.
111 |
112 | ### Linting your app code
113 |
114 | Linting is the automated checking of source code for programmatic and stylistic errors. This is done by using a lint tool (otherwise known as a linter). Linting is important to reduce errors and improve the overall quality of your code, especially for repositories with multiple developers or public repositories.
115 |
116 | You can add automated linting with Ruff by passing ruff: true
to Streamlit App Action.
117 |
118 | yaml 119 | - uses: streamlit/[email protected] 120 | with: 121 | app-path: streamlit_app.py 122 | ruff: true 123 |
124 |
125 |
126 |
127 | You may want to add a pre-commit hook like ruff-pre-commit in your local development environment to fix linting errors before they get to CI.
128 |
129 |
130 |
131 | ### Viewing results
132 |
133 | If tests fail, the CI workflow will fail and you will see the results in GitHub. Console logs are available by clicking into the workflow run as described here.
134 |
135 |
136 |
137 | For higher-level test results, you can use pytest-results-action. You can combine this with Streamlit App Action as follows:
138 |
139 |
yaml 140 | # ... setup as above ... 141 | - uses: streamlit/[email protected] 142 | with: 143 | app-path: streamlit_app.py 144 | # Add pytest-args to output junit xml 145 | pytest-args: -v --junit-xml=test-results.xml 146 | - if: always() 147 | uses: pmeier/[email protected] 148 | with: 149 | path: test-results.xml 150 | summary: true 151 | display-options: fEX 152 |
153 |
154 |
155 |
156 | ## Writing your own actions
157 |
158 | The above is just provided as an example. Streamlit App Action is a quick way to get started. Once you learn the basics of your CI tool of choice, it's easy to build and customize your own automated workflows. This is a great way to improve your overall productivity as a developer and the quality of your apps.
159 |
160 | ## Working example
161 |
162 | As a final working example example, take a look at our
streamlit/llm-examples
Actions, defined in this workflow file.
163 |
1 | ---
2 | title: Beyond the basics of app testing
3 | slug: /develop/concepts/app-testing/beyond-the-basics
4 | ---
5 |
6 | # Beyond the basics of app testing
7 |
8 | Now that you're comfortable with executing a basic test for a Streamlit app let's cover the mutable attributes of AppTest
:
9 |
10 | - AppTest.secrets
11 | - AppTest.session_state
12 | - AppTest.query_params
13 |
14 | You can read and update values using dict-like syntax for all three attributes. For .secrets
and .query_params
, you can use key notation but not attribute notation. For example, the .secrets
attribute for AppTest
accepts at.secrets["my_key"]
but not at.secrets.my_key
. This differs from how you can use the associated command in the main library. On the other hand, .session_state
allows both key notation and attribute notation.
15 |
16 | For these attributes, the typical pattern is to declare any values before executing the app's first run. Values can be inspected at any time in a test. There are a few extra considerations for secrets and Session State, which we'll cover now.
17 |
18 | ## Using secrets with app testing
19 |
20 | Be careful not to include secrets directly in your tests. Consider this simple project with pytest
executed in the project's root directory:
21 |
22 | none 23 | myproject/ 24 | ├── .streamlit/ 25 | │ ├── config.toml 26 | │ └── secrets.toml 27 | ├── app.py 28 | └── tests/ 29 | └── test_app.py 30 |
31 |
32 | bash 33 | cd myproject 34 | pytest tests/ 35 |
36 |
37 | In the above scenario, your simulated app will have access to your secrets.toml
file. However, since you don't want to commit your secrets to your repository, you may need to write tests where you securely pull your secrets into memory or use dummy secrets.
38 |
39 | ### Example: declaring secrets in a test
40 |
41 | Within a test, declare each secret after initializing your AppTest
instance but before the first run. (A missing secret may result in an app that doesn't run!) For example, consider the following secrets file and corresponding test initialization to assign the same secrets manually:
42 |
43 | Secrets file:
44 |
45 | toml 46 | db_username = "Jane" 47 | db_password = "mypassword" 48 | 49 | [my_other_secrets] 50 | things_i_like = ["Streamlit", "Python"] 51 |
52 |
53 | Testing file with equivalent secrets:
54 |
55 | python 56 | # Initialize an AppTest instance. 57 | at = AppTest.from_file("app.py") 58 | # Declare the secrets. 59 | at.secrets["db_username"] = "Jane" 60 | at.secrets["db_password"] = "mypassword" 61 | at.secrets["my_other_secrets.things_i_like"] = ["Streamlit", "Python"] 62 | # Run the app. 63 | at.run() 64 |
65 |
66 | Generally, you want to avoid typing your secrets directly into your test. If you don't need your real secrets for your test, you can declare dummy secrets as in the example above. If your app uses secrets to connect to an external service like a database or API, consider mocking that service in your app tests. If you need to use the real secrets and actually connect, you should use an API to pass them securely and anonymously. If you are automating your tests with GitHub actions, check out their Security guide.
67 |
68 | python 69 | at.secrets["my_key"] = <value provided through API> 70 |
71 |
72 | ## Working with Session State in app testing
73 |
74 | The .session_state
attribute for AppTest
lets you read and update Session State values using key notation (at.session_state["my_key"]
) and attribute notation (at.session_state.my_key
). By manually declaring values in Session State, you can directly jump to a specific state instead of simulating many steps to get there. Additionally, the testing framework does not provide native support for multipage apps. An instance of AppTest
can only test one page. You must manually declare Session State values to simulate a user carrying data from another page.
75 |
76 | ### Example: testing a multipage app
77 |
78 | Consider a simple multipage app where the first page can modify a value in Session State. To test the second page, set Session State manually and run the simulated app within the test:
79 |
80 | Project structure:
81 |
82 | none 83 | myproject/ 84 | ├── pages/ 85 | │ └── second.py 86 | ├── first.py 87 | └── tests/ 88 | └── test_second.py 89 |
90 |
91 | First app page:
92 |
93 | python 94 | """first.py""" 95 | import streamlit as st 96 | 97 | st.session_state.magic_word = st.session_state.get("magic_word", "Streamlit") 98 | 99 | new_word = st.text_input("Magic word:") 100 | 101 | if st.button("Set the magic word"): 102 | st.session_state.magic_word = new_word 103 |
104 |
105 | Second app page:
106 |
107 | python 108 | """second.py""" 109 | import streamlit as st 110 | 111 | st.session_state.magic_word = st.session_state.get("magic_word", "Streamlit") 112 | 113 | if st.session_state.magic_word == "Balloons": 114 | st.markdown(":balloon:") 115 |
116 |
117 | Testing file:
118 |
119 | python 120 | """test_second.py""" 121 | from streamlit.testing.v1 import AppTest 122 | 123 | def test_balloons(): 124 | at = AppTest.from_file("pages/second.py") 125 | at.session_state["magic_word"] = "Balloons" 126 | at.run() 127 | assert at.markdown[0].value == ":balloon:" 128 |
129 |
130 | By setting the value at.session_state["magic_word"] = "Balloons"
within the test, you can simulate a user navigating to second.py
after entering and saving "Balloons" on first.py
.
131 |
1 | ---
2 | title: App testing cheat sheet
3 | slug: /develop/concepts/app-testing/cheat-sheet
4 | ---
5 |
6 | # App testing cheat sheet
7 |
8 | ## Text elements
9 |
10 | python 11 | from streamlit.testing.v1 import AppTest 12 | 13 | at = AppTest.from_file("cheatsheet_app.py") 14 | 15 | # Headers 16 | assert "My app" in at.title[0].value 17 | assert "New topic" in at.header[0].value 18 | assert "Interesting sub-topic" in at.subheader[0].value 19 | assert len(at.divider) == 2 20 | 21 | # Body / code 22 | assert "Hello, world!" in at.markdown[0].value 23 | assert "import streamlit as st" in at.code[0].value 24 | assert "A cool diagram" in at.caption[0].value 25 | assert "Hello again, world!" in at.text[0].value 26 | assert "\int a x^2 \,dx" in at.latex[0].value 27 |
28 |
29 | ## Input widgets
30 |
31 | python 32 | from streamlit.testing.v1 import AppTest 33 | 34 | at = AppTest.from_file("cheatsheet_app.py") 35 | 36 | # button 37 | assert at.button[0].value == False 38 | at.button[0].click().run() 39 | assert at.button[0].value == True 40 | 41 | # checkbox 42 | assert at.checkbox[0].value == False 43 | at.checkbox[0].check().run() # uncheck() is also supported 44 | assert at.checkbox[0].value == True 45 | 46 | # color_picker 47 | assert at.color_picker[0].value == "#FFFFFF" 48 | at.color_picker[0].pick("#000000").run() 49 | 50 | # date_input 51 | assert at.date_input[0].value == datetime.date(2019, 7, 6) 52 | at.date_input[0].set_value(datetime.date(2022, 12, 21)).run() 53 | 54 | # form_submit_button - shows up just like a button 55 | assert at.button[0].value == False 56 | at.button[0].click().run() 57 | assert at.button[0].value == True 58 | 59 | # multiselect 60 | assert at.multiselect[0].value == ["foo", "bar"] 61 | at.multiselect[0].select("baz").unselect("foo").run() 62 | 63 | # number_input 64 | assert at.number_input[0].value == 5 65 | at.number_input[0].increment().run() 66 | 67 | # radio 68 | assert at.radio[0].value == "Bar" 69 | assert at.radio[0].index == 3 70 | at.radio[0].set_value("Foo").run() 71 | 72 | # selectbox 73 | assert at.selectbox[0].value == "Bar" 74 | assert at.selectbox[0].index == 3 75 | at.selectbox[0].set_value("Foo").run() 76 | 77 | # select_slider 78 | assert at.select_slider[0].value == "Feb" 79 | at.select_slider[0].set_value("Mar").run() 80 | at.select_slider[0].set_range("Apr", "Jun").run() 81 | 82 | # slider 83 | assert at.slider[0].value == 2 84 | at.slider[0].set_value(3).run() 85 | at.slider[0].set_range(4, 6).run() 86 | 87 | # text_area 88 | assert at.text_area[0].value == "Hello, world!" 89 | at.text_area[0].set_value("Hello, yourself!").run() 90 | 91 | # text_input 92 | assert at.text_input[0].value == "Hello, world!") 93 | at.text_input[0].set_value("Hello, yourself!").run() 94 | 95 | # time_input 96 | assert at.time_input[0].value == datetime.time(8, 45) 97 | at.time_input[0].set_value(datetime.time(12, 30)) 98 | 99 | # toggle 100 | assert at.toggle[0].value == False 101 | assert at.toggle[0].label == "Debug mode" 102 | at.toggle[0].set_value(True).run() 103 | assert at.toggle[0].value == True 104 |
105 |
106 | ## Data elements
107 |
108 | python 109 | from streamlit.testing.v1 import AppTest 110 | 111 | at = AppTest.from_file("cheatsheet_app.py") 112 | 113 | # dataframe 114 | expected_df = pd.DataFrame([1, 2, 3]) 115 | assert at.dataframe[0].value.equals(expected_df) 116 | 117 | # metric 118 | assert at.metric[0].value == "9500" 119 | assert at.metric[0].delta == "1000" 120 | 121 | # json 122 | assert at.json[0].value == '["hi", {"foo": "bar"}]' 123 | 124 | # table 125 | table_df = pd.DataFrame([1, 2, 3]) 126 | assert at.table[0].value.equals(table_df) 127 |
128 |
129 | ## Layouts and containers
130 |
131 | python 132 | from streamlit.testing.v1 import AppTest 133 | 134 | at = AppTest.from_file("cheatsheet_app.py") 135 | 136 | # sidebar 137 | at.sidebar.text_input[0].set_value("Jane Doe") 138 | 139 | # columns 140 | at.columns[1].markdown[0].value == "Hello, world!" 141 | 142 | # tabs 143 | at.tabs[2].markdown[0].value == "Hello, yourself!" 144 |
145 |
146 | ## Chat elements
147 |
148 | python 149 | from streamlit.testing.v1 import AppTest 150 | 151 | at = AppTest.from_file("cheatsheet_app.py") 152 | 153 | # chat_input 154 | at.chat_input[0].set_value("Do you know any jokes?").run() 155 | # Note: chat_input value clears after every re-run (like in a real app) 156 | 157 | # chat_message 158 | assert at.chat_message[0].markdown[0].value == "Do you know any jokes?" 159 | assert at.chat_message[0].avatar == "user" 160 |
161 |
162 | ## Status elements
163 |
164 | python 165 | from streamlit.testing.v1 import AppTest 166 | 167 | at = AppTest.from_file("cheatsheet_app.py") 168 | 169 | # exception 170 | assert len(at.exception) == 1 171 | assert "TypeError" in at.exception[0].value 172 | 173 | # Other in-line alerts: success, info, warning, error 174 | assert at.success[0].value == "Great job!" 175 | assert at.info[0].value == "Please enter an API key to continue" 176 | assert at.warning[0].value == "Sorry, the passwords didn't match" 177 | assert at.error[0].value == "Something went wrong :(" 178 | 179 | # toast 180 | assert at.toast[0].value == "That was lit!" and at.toast[0].icon == "🔥" 181 |
182 |
183 | ## Limitations
184 |
185 | As of Streamlit 1.28, the following Streamlit features are not natively supported by AppTest
. However, workarounds are possible for many of them by inspecting the underlying proto directly using AppTest.get()
. We plan to regularly add support for missing elements until all features are supported.
186 |
187 | - Chart elements (st.bar_chart
, st.line_chart
, etc)
188 | - Media elements (st.image
, st.video
, st.audio
)
189 | - st.file_uploader
190 | - st.data_editor
191 | - st.expander
192 | - st.status
193 | - st.camera_input
194 | - st.download_button
195 | - st.link_button
196 |
1 | ---
2 | title: App testing example
3 | slug: /develop/concepts/app-testing/examples
4 | ---
5 |
6 | # App testing example
7 |
8 | ## Testing a login page
9 |
10 | Let's consider a login page. In this example, secrets.toml
is not present. We'll manually declare dummy secrets directly in the tests. To avoid timing attacks, the login script uses hmac
to compare a user's password to the secret value as a security best practice.
11 |
12 | ### Project summary
13 |
14 | #### Login page behavior
15 |
16 | Before diving into the app's code, let's think about what this page is supposed to do. Whether you use test-driven development or you write unit tests after your code, it's a good idea to think about the functionality that needs to be tested. The login page should behave as follows:
17 |
18 | - Before a user interacts with the app:
19 | - Their status is "unverified."
20 | - A password prompt is displayed.
21 | - If a user types an incorrect password:
22 | - Their status is "incorrect."
23 | - An error message is displayed.
24 | - The password attempt is cleared from the input.
25 | - If a user types a correct password:
26 | - Their status is "verified."
27 | - A confirmation message is displayed.
28 | - A logout button is displayed (without a login prompt).
29 | - If a logged-in user clicks the Log out button:
30 | - Their status is "unverified."
31 | - A password prompt is displayed.
32 |
33 | #### Login page project structure
34 |
35 | none 36 | myproject/ 37 | ├── app.py 38 | └── tests/ 39 | └── test_app.py 40 |
41 |
42 | #### Login page Python file
43 |
44 | The user's status mentioned in the page's specifications are encoded in st.session_state.status
. This value is initialized at the beginning of the script as "unverified" and is updated through a callback when the password prompt receives a new entry.
45 |
46 | python 47 | """app.py""" 48 | import streamlit as st 49 | import hmac 50 | 51 | st.session_state.status = st.session_state.get("status", "unverified") 52 | st.title("My login page") 53 | 54 | 55 | def check_password(): 56 | if hmac.compare_digest(st.session_state.password, st.secrets.password): 57 | st.session_state.status = "verified" 58 | else: 59 | st.session_state.status = "incorrect" 60 | st.session_state.password = "" 61 | 62 | def login_prompt(): 63 | st.text_input("Enter password:", key="password", on_change=check_password) 64 | if st.session_state.status == "incorrect": 65 | st.warning("Incorrect password. Please try again.") 66 | 67 | def logout(): 68 | st.session_state.status = "unverified" 69 | 70 | def welcome(): 71 | st.success("Login successful.") 72 | st.button("Log out", on_click=logout) 73 | 74 | 75 | if st.session_state.status != "verified": 76 | login_prompt() 77 | st.stop() 78 | welcome() 79 |
80 |
81 | #### Login page test file
82 |
83 | These tests closely follow the app's specifications above. In each test, a dummy secret is set before running the app and proceeding with further simulations and checks.
84 |
85 | python 86 | from streamlit.testing.v1 import AppTest 87 | 88 | def test_no_interaction(): 89 | at = AppTest.from_file("app.py") 90 | at.secrets["password"] = "streamlit" 91 | at.run() 92 | assert at.session_state["status"] == "unverified" 93 | assert len(at.text_input) == 1 94 | assert len(at.warning) == 0 95 | assert len(at.success) == 0 96 | assert len(at.button) == 0 97 | assert at.text_input[0].value == "" 98 | 99 | def test_incorrect_password(): 100 | at = AppTest.from_file("app.py") 101 | at.secrets["password"] = "streamlit" 102 | at.run() 103 | at.text_input[0].input("balloon").run() 104 | assert at.session_state["status"] == "incorrect" 105 | assert len(at.text_input) == 1 106 | assert len(at.warning) == 1 107 | assert len(at.success) == 0 108 | assert len(at.button) == 0 109 | assert at.text_input[0].value == "" 110 | assert "Incorrect password" in at.warning[0].value 111 | 112 | def test_correct_password(): 113 | at = AppTest.from_file("app.py") 114 | at.secrets["password"] = "streamlit" 115 | at.run() 116 | at.text_input[0].input("streamlit").run() 117 | assert at.session_state["status"] == "verified" 118 | assert len(at.text_input) == 0 119 | assert len(at.warning) == 0 120 | assert len(at.success) == 1 121 | assert len(at.button) == 1 122 | assert "Login successful" in at.success[0].value 123 | assert at.button[0].label == "Log out" 124 | 125 | def test_log_out(): 126 | at = AppTest.from_file("app.py") 127 | at.secrets["password"] = "streamlit" 128 | at.session_state["status"] = "verified" 129 | at.run() 130 | at.button[0].click().run() 131 | assert at.session_state["status"] == "unverified" 132 | assert len(at.text_input) == 1 133 | assert len(at.warning) == 0 134 | assert len(at.success) == 0 135 | assert len(at.button) == 0 136 | assert at.text_input[0].value == "" 137 |
138 |
139 | See how Session State was modified in the last test? Instead of fully simulating a user logging in, the test jumps straight to a logged-in state by setting at.session_state["status"] = "verified"
. After running the app, the test proceeds to simulate the user logging out.
140 |
141 | ### Automating your tests
142 |
143 | If myproject/
was pushed to GitHub as a repository, you could add GitHub Actions test automation with Streamlit App Action. This is as simple as adding a workflow file at myproject/.github/workflows/
:
144 |
145 | yaml 146 | # .github/workflows/streamlit-app.yml 147 | name: Streamlit app 148 | 149 | on: 150 | push: 151 | branches: ["main"] 152 | pull_request: 153 | branches: ["main"] 154 | 155 | permissions: 156 | contents: read 157 | 158 | jobs: 159 | streamlit: 160 | runs-on: ubuntu-latest 161 | steps: 162 | - uses: actions/checkout@v4 163 | - uses: actions/setup-python@v5 164 | with: 165 | python-version: "3.11" 166 | - uses: streamlit/[email protected] 167 | with: 168 | app-path: app.py 169 |
170 |
1 | ---
2 | title: Get started with app testing
3 | slug: /develop/concepts/app-testing/get-started
4 | ---
5 |
6 | # Get started with app testing
7 |
8 | This guide will cover a simple example of how tests are structured within a project and how to execute them with pytest
. After seeing the big picture, keep reading to learn about the Fundamentals of app testing:
9 |
10 | - Initializing and running a simulated app
11 | - Retrieving elements
12 | - Manipulating widgets
13 | - Inspecting the results
14 |
15 | Streamlit's app testing framework is not tied to any particular testing tool, but we'll use pytest
for our examples since it is one of the most common Python test frameworks. To try out the examples in this guide, be sure to install pytest
into your Streamlit development environment before you begin:
16 |
17 | bash 18 | pip install pytest 19 |
20 |
21 | ## A simple testing example with pytest
22 |
23 | This section explains how a simple test is structured and executed with pytest
. For a comprehensive introduction to pytest
, check out Real Python's guide to Effective Python testing with pytest.
24 |
25 | ### How pytest
is structured
26 |
27 | pytest
uses a naming convention for files and functions to execute tests conveniently. Name your test scripts of the form test_<name>.py
or <name>_test.py
. For example, you can use test_myapp.py
or myapp_test.py
. Within your test scripts, each test is written as a function. Each function is named to begin or end with test
. We will prefix all our test scripts and test functions with test_
for our examples in this guide.
28 |
29 | You can write as many tests (functions) within a single test script as you want. When calling pytest
in a directory, all test_<name>.py
files within it will be used for testing. This includes files within subdirectories. Each test_<something>
function within those files will be executed as a test. You can place test files anywhere in your project directory, but it is common to collect tests into a designated tests/
directory. For other ways to structure and execute tests, check out How to invoke pytest in the pytest
docs.
30 |
31 | ### Example project with app testing
32 |
33 | Consider the following project:
34 |
35 | none 36 | myproject/ 37 | ├── app.py 38 | └── tests/ 39 | └── test_app.py 40 |
41 |
42 | Main app file:
43 |
44 | python 45 | """app.py""" 46 | import streamlit as st 47 | 48 | # Initialize st.session_state.beans 49 | st.session_state.beans = st.session_state.get("beans", 0) 50 | 51 | st.title("Bean counter :paw_prints:") 52 | 53 | addend = st.number_input("Beans to add", 0, 10) 54 | if st.button("Add"): 55 | st.session_state.beans += addend 56 | st.markdown(f"Beans counted: {st.session_state.beans}") 57 |
58 |
59 | Testing file:
60 |
61 | python 62 | """test_app.py""" 63 | from streamlit.testing.v1 import AppTest 64 | 65 | def test_increment_and_add(): 66 | """A user increments the number input, then clicks Add""" 67 | at = AppTest.from_file("app.py").run() 68 | at.number_input[0].increment().run() 69 | at.button[0].click().run() 70 | assert at.markdown[0].value == "Beans counted: 1" 71 |
72 |
73 | Let's take a quick look at what's in this app and test before we run it. The main app file (app.py
) contains four elements when rendered: st.title
, st.number_input
, st.button
, and st.markdown
. The test script (test_app.py
) includes a single test (the function named test_increment_and_add
). We'll cover test syntax in more detail in the latter half of this guide, but here's a brief explanation of what this test does:
74 |
75 | 1. Initialize the simulated app and execute the first script run.
76 | python 77 | at = AppTest.from_file("app.py").run() 78 |
79 | 2. Simulate a user clicking the plus icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>add) to increment the number input (and the resulting script rerun).
80 | python 81 | at.number_input[0].increment().run() 82 |
83 | 3. Simulate a user clicking the "Add" button (and the resulting script rerun).
84 | python 85 | at.button[0].click().run() 86 |
87 | 4. Check if the correct message is displayed at the end.
88 | python 89 | assert at.markdown[0].value == "Beans counted: 1" 90 |
91 |
92 | Assertions are the heart of tests. When the assertion is true, the test passes. When the assertion is false, the test fails. A test can have multiple assertions, but keeping tests tightly focused is good practice. When tests focus on a single behavior, it is easier to understand and respond to failure.
93 |
94 | ### Try out a simple test with pytest
95 |
96 | 1. Copy the files above into a new "myproject" directory.
97 | 2. Open a terminal and change directory to your project.
98 | bash 99 | cd myproject 100 |
101 | 3. Execute pytest
:
102 | bash 103 | pytest 104 |
105 |
106 | The test should execute successfully. Your terminal should show something like this:
107 |
108 |
109 |
110 | By executing
pytest
at the root of your project directory, all Python files with the test prefix (test_<name>.py
) will be scanned for test functions. Within each test file, each function with the test prefix will be executed as a test. pytest
then counts successes and itemizes failures. You can also direct pytest
to only scan your testing directory. For example, from the root of your project directory, execute:
111 |
112 | bash 113 | pytest tests/ 114 |
115 |
116 | ### Handling file paths and imports with pytest
117 |
118 | Imports and paths within a test script should be relative to the directory where pytest
is called. That is why the test function uses the path app.py
instead of ../app.py
even though the app file is one directory up from the test script. You'll usually call pytest
from the directory containing your main app file. This is typically the root of your project directory.
119 |
120 | Additionally, if .streamlit/
is present in the directory where you call pytest
, any config.toml
and secrets.toml
within it will be accessible to your simulated app. For example, your simulated app will have access to the config.toml
and secrets.toml
files in this common setup:
121 |
122 | Project structure:
123 |
124 | none 125 | myproject/ 126 | ├── .streamlit/ 127 | │ ├── config.toml 128 | │ └── secrets.toml 129 | ├── app.py 130 | └── tests/ 131 | └── test_app.py 132 |
133 |
134 | Initialization within test_app.py
:
135 |
136 | python 137 | # Path to app file is relative to myproject/ 138 | at = AppTest.from_file("app.py").run() 139 |
140 |
141 | Command to execute tests:
142 |
143 | bash 144 | cd myproject 145 | pytest tests/ 146 |
147 |
148 | ## Fundamentals of app testing
149 |
150 | Now that you understand the basics of pytest
let's dive into using Streamlit's app testing framework. Every test begins with initializing and running your simulated app. Additional commands are used to retrieve, manipulate, and inspect elements.
151 |
152 | On the next page, we'll go Beyond the basics and cover more advanced scenarios like working with secrets, Session State, or multipage apps.
153 |
154 | ### How to initialize and run a simulated app
155 |
156 | To test a Streamlit app, you must first initialize an instance of AppTest
with the code for one page of your app. There are three methods for initializing a simulated app. These are provided as class methods to AppTest
. We will focus on AppTest.from_file()
which allows you to provide a path to a page of your app. This is the most common scenario for building automated tests during app development. AppTest.from_string()
and AppTest.from_function()
may be helpful for some simple or experimental scenarios.
157 |
158 | Let's continue with the example from above.
159 |
160 | Recall the testing file:
161 |
162 | python 163 | """test_app.py""" 164 | from streamlit.testing.v1 import AppTest 165 | 166 | def test_increment_and_add(): 167 | """A user increments the number input, then clicks Add""" 168 | at = AppTest.from_file("app.py").run() 169 | at.number_input[0].increment().run() 170 | at.button[0].click().run() 171 | assert at.markdown[0].value == "Beans counted: 1" 172 |
173 |
174 | Look at the first line in the test function:
175 |
176 | python 177 | at = AppTest.from_file("app.py").run() 178 |
179 |
180 | This is doing two things and is equivalent to:
181 |
182 | python 183 | # Initialize the app. 184 | at = AppTest.from_file("app.py") 185 | # Run the app. 186 | at.run() 187 |
188 |
189 | AppTest.from_file()
returns an instance of AppTest
, initialized with the contents of app.py
. The .run()
method is used to run the app for the first time. Looking at the test, notice that the .run()
method manually executes each script run. A test must explicitly run the app each time. This applies to the app's first run and any rerun resulting from simulated user input.
190 |
191 | ### How to retrieve elements
192 |
193 | The attributes of the AppTest
class return sequences of elements. The elements are sorted according to display order in the rendered app. Specific elements can be retrieved by index. Additionally, widgets with keys can be retrieved by key.
194 |
195 | #### Retrieve elements by index
196 |
197 | Each attribute of AppTest
returns a sequence of the associated element type. Specific elements can be retrieved by index. In the above example, at.number_input
returns a sequence of all st.number_input
elements in the app. Thus, at.number_input[0]
is the first such element in the app. Similarly, at.markdown
returns a collection of all st.markdown
elements where at.markdown[0]
is the first such element.
198 |
199 | Check out the current list of supported elements in the "Attributes" section of the AppTest
class or the App testing cheat sheet. You can also use the .get()
method and pass the attribute's name. at.get("number_input")
and at.get("markdown")
are equivalent to at.number_input
and at.markdown
, respectively.
200 |
201 | The returned sequence of elements is ordered by appearance on the page. If containers are used to insert elements in a different order, these sequences may not match the order within your code. Consider the following example where containers are used to switch the order of two buttons on the page:
202 |
203 | python 204 | import streamlit as st 205 | 206 | first = st.container() 207 | second = st.container() 208 | 209 | second.button("A") 210 | first.button("B") 211 |
212 |
213 | If the above app was tested, the first button (at.button[0]
) would be labeled "B" and the second button (at.button[1]
) would be labeled "A." As true assertions, these would be:
214 |
215 | python 216 | assert at.button[0].label == "B" 217 | assert at.button[1].label == "A" 218 |
219 |
220 | #### Retrieve widgets by key
221 |
222 | You can retrieve keyed widgets by their keys instead of their order on the page. The key of the widget is passed as either an arg or kwarg. For example, look at this app and the following (true) assertions:
223 |
224 | python 225 | import streamlit as st 226 | 227 | st.button("Next", key="submit") 228 | st.button("Back", key="cancel") 229 |
230 |
231 | python 232 | assert at.button(key="submit").label == "Next" 233 | assert at.button("cancel").label == "Back" 234 |
235 |
236 | #### Retrieve containers
237 |
238 | You can also narrow down your sequences of elements by retrieving specific containers. Each retrieved container has the same attributes as AppTest
. For example, at.sidebar.checkbox
returns a sequence of all checkboxes in the sidebar. at.main.selectbox
returns the sequence of all selectboxes in the main body of the app (not in the sidebar).
239 |
240 | For AppTest.columns
and AppTest.tabs
, a sequence of containers is returned. So at.columns[0].button
would be the sequence of all buttons in the first column appearing in the app.
241 |
242 | ### How to manipulate widgets
243 |
244 | All widgets have a universal .set_value()
method. Additionally, many widgets have specific methods for manipulating their value. The names of Testing element classes closely match the names of the AppTest
attributes. For example, look at the return type of AppTest.button
to see the corresponding class of Button
. Aside from setting the value of a button with .set_value()
, you can also use .click()
. Check out each testing element class for its specific methods.
245 |
246 | ### How to inspect elements
247 |
248 | All elements, including widgets, have a universal .value
property. This returns the contents of the element. For widgets, this is the same as the return value or value in Session State. For non-input elements, this will be the value of the primary contents argument. For example, .value
returns the value of body
for st.markdown
or st.error
. It returns the value of data
for st.dataframe
or st.table
.
249 |
250 | Additionally, you can check many other details for widgets like labels or disabled status. Many parameters are available for inspection, but not all. Use linting software to see what is currently supported. Here's an example:
251 |
252 | python 253 | import streamlit as st 254 | 255 | st.selectbox("A", [1,2,3], None, help="Pick a number", placeholder="Pick me") 256 |
257 |
258 | python 259 | assert at.selectbox[0].value == None 260 | assert at.selectbox[0].label == "A" 261 | assert at.selectbox[0].options == ["1","2","3"] 262 | assert at.selectbox[0].index == None 263 | assert at.selectbox[0].help == "Pick a number" 264 | assert at.selectbox[0].placeholder == "Pick me" 265 | assert at.selectbox[0].disabled == False 266 |
267 |
268 |
269 |
270 | Note that the options
for st.selectbox
were declared as integers but asserted as strings. As noted in the documentation for st.selectbox
, options are cast internally to strings. If you ever find yourself getting unexpected results, check the documentation carefully for any notes about recasting types internally.
271 |
272 |
273 |
1 | --- 2 | title: Working with Streamlit's execution model 3 | slug: /develop/concepts/architecture 4 | --- 5 | 6 | # Working with Streamlit's execution model 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | Understand how to start your Streamlit app. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Understand Streamlit's client-server architecture and related considerations. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Every Streamlit app has a few widgets in the top right to help you as you develop your app and help your users as they view your app. This is called the app chrome. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Make your app performant by caching results to avoid unecessary recomputation with each rerun. 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Manage your app's statefulness with Session State. 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Use forms to isolate user input and prevent unnecessary app reruns. 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Understand how widgets work in detail. 63 | 64 | 65 | 66 | 67 |1 | ---
2 | title: The app chrome
3 | slug: /develop/concepts/architecture/app-chrome
4 | ---
5 |
6 | # The app chrome
7 |
8 | Your Streamlit app has a few widgets in the top right to help you as you develop. These widgets also help your viewers as they use your app. We call this things “the app chrome”. The chrome includes a status area, toolbar, and app menu.
9 |
10 | Your app menu is configurable. By default, you can access developer options from the app menu when viewing an app locally or on Streamlit Community Cloud while logged into an account with administrative access. While viewing an app, click the icon in the upper-right corner to access the menu.
11 |
12 |
13 |
14 | ## Menu options
15 |
16 | The menu is split into two sections. The upper section contains options available to all viewers and the lower section contains options for developers. Read more about customizing this menu at the end of this page.
17 |
18 | ### Rerun
19 |
20 | You can manually trigger a rerun of your app by clicking "Rerun" from the app menu. This rerun will not reset your session. Your widget states and values stored in
st.session_state
will be preserved. As a shortcut, without opening the app menu, you can rerun your app by pressing "R" on your keyboard (if you aren't currently focused on an input element).
21 |
22 | ### Settings
23 |
24 | With the "Settings" option, you can control the appearance of your app while it is running. If viewing the app locally, you can set how your app responds to changes in your source code. See more about development flow in Basic concepts. You can also force your app to appear in wide mode, even if not set within the script using st.set_page_config
.
25 |
26 | #### Theme settings
27 |
28 | After clicking "Settings" from the app menu, you can choose between "Light", "Dark", or "Use system setting" for the app's base theme. Click "Edit active theme" to modify the theme, color-by-color.
29 |
30 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
31 |
32 |
33 |
34 |
35 |
36 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
37 |
38 |
39 |
40 | ### Print
41 |
42 | Click "Print" or use keyboard shortcuts (
⌘+P
or Ctrl+P
) to open a print dialog. This option uses your browser's built-in print-to-pdf function. To modify the appearance of your print, you can do the following:
43 |
44 | - Expand or collapse the sidebar before printing to respectively include or exclude it from the print.
45 | - Resize the sidebar in your app by clicking and dragging its right border to achieve your desired width.
46 | - You may need to enable "Background graphics" in your print dialog if you are printing in dark mode.
47 | - You may need to disable wide mode in Settings or adjust the print scale to prevent elements from clipping off the page.
48 |
49 | ### Record a screencast
50 |
51 | You can easily make screen recordings right from your app! Screen recording is supported in the latest versions of Chrome, Edge, and Firefox. Ensure your browser is up-to-date for compatibility. Depending on your current settings, you may need to grant permission to your browser to record your screen or to use your microphone if recording a voiceover.
52 |
53 | 1. While viewing your app, open the app menu from the upper-right corner.
54 | 2. Click "Record a screencast."
55 | 3. If you want to record audio through your microphone, check "Also record audio."
56 | 4. Click "Start recording." (You may be prompted by your OS to permit your browser to record your screen or use your microphone.)
57 |
58 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
59 |
60 |
61 |
62 | 5. Select which tab, window, or monitor you want to record from the listed options. The interface will vary depending on your browser.
63 |
64 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
65 |
66 |
67 |
68 | 6. Click "Share."
69 |
70 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
71 |
72 |
73 |
74 | 7. While recording, you will see a red circle on your app's tab and on the app menu icon. If you want to cancel the recording, click "Stop sharing" at the bottom of your app.
75 |
76 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
77 |
78 |
79 |
80 | 8. When you are done recording, press "Esc" on your keyboard or click "Stop recording" from your app's menu.
81 |
82 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
83 |
84 |
85 |
86 | 9. Follow your browser's instructions to save your recording. Your saved recording will be available where your browser saves downloads.
87 |
88 | The whole process looks like this:
89 |
90 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
91 |
92 |
93 |
94 | ### About
95 |
96 | You can conveniently check what version of Streamlit is running from the "About" option. Developers also have the option to customize the message shown here using
st.set_page_config
.
97 |
98 | ## Developer options
99 |
100 | By default, developer options only show when viewing an app locally or when viewing a Community Cloud app while logged in with administrative permission. You can customize the menu if you want to make these options available for all users.
101 |
102 | ### Clear cache
103 |
104 | Reset your app's cache by clicking "Clear cache" from the app's menu or by pressing "C" on your keyboard while not focused on an input element. This will remove all cached entries for @st.cache_data
and @st.cache_resource
.
105 |
106 | ### Deploy this app
107 |
108 | If you are running an app locally from within a git repo, you can deploy your app to Streamlit Community Cloud in a few easy clicks! Make sure your work has been pushed to your online GitHub repository before beginning. For the greatest convenience, make sure you have already created your Community Cloud account and are signed in.
109 |
110 | 1. Click "Deploy" next to the app menu icon (<i style={{ verticalAlign: "-.25em" }} className={{ class: "material-icons-sharp" }}>more_vert).
111 |
112 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
113 |
114 |
115 |
116 | 2. Click "Deploy now."
117 |
118 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
119 |
120 |
121 |
122 | 3. You will be taken to Community Cloud's "Deploy an app" page. Your app's repository, branch, and file name will be prefilled to match your current app! Learn more about deploying an app on Streamlit Community Cloud.
123 |
124 | The whole process looks like this:
125 |
126 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}>
127 |
128 |
129 |
130 | ## Customize the menu
131 |
132 | Using
client.toolbarMode
in your app's configuration, you can make the app menu appear in the following ways:
133 |
134 | - "developer"
— Show the developer options to all viewers.
135 | - "viewer"
— Hide the developer options from all viewers.
136 | - "minimal"
— Show only those options set externally. These options can be declared through st.set_page_config
or populated through Streamlit Community Cloud.
137 | - "auto"
— This is the default and will show the developer options when accessed through localhost or through Streamlit Community Cloud when logged into an administrative account for the app. Otherwise, the developer options will not show.
138 |
1 | ---
2 | title: Understanding Streamlit's client-server architecture
3 | slug: /develop/concepts/architecture/architecture
4 | ---
5 |
6 | # Understanding Streamlit's client-server architecture
7 |
8 | Streamlit apps have a client-server structure. The Python backend of your app is the server. The frontend you view through a browser is the client. When you develop an app locally, your computer runs both the server and the client. If someone views your app across a local or global network, the server and client run on different machines. If you intend to share or deploy your app, it's important to understand this client-server structure to avoid common pitfalls.
9 |
10 | ## Python backend (server)
11 |
12 | When you execute the command streamlit run your_app.py
, your computer uses Python to start up a Streamlit server. This server is the brains of your app and performs the computations for all users who view your app. Whether users view your app across a local network or the internet, the Streamlit server runs on the one machine where the app was initialized with streamlit run
. The machine running your Streamlit server is also called a host.
13 |
14 | ## Browser frontend (client)
15 |
16 | When someone views your app through a browser, their device is a Streamlit client. When you view your app from the same computer where you are running or developing your app, then server and client are coincidentally running on the same machine. However, when users view your app across a local network or the internet, the client runs on a different machine from the server.
17 |
18 | ## Server-client impact on app design
19 |
20 | Keep in mind the following considerations when building your Streamlit app:
21 |
22 | - The computer running or hosting your Streamlit app is responsible for providing the compute and storage necessary to run your app for all users and must be sized appropriately to handle concurrent users.
23 | - Your app will not have access to a user's files, directories, or OS. Your app can only work with specific files a user has uploaded to your app through a widget like st.file_uploader
.
24 | - If your app communicates with any peripheral devices (like cameras), you must use Streamlit commands or custom components that will access those devices through the user's browser and correctly communicate between the client (frontend) and server (backend).
25 | - If your app opens or uses any program or process outside of Python, they will run on the server. For example, you may want to use webrowser
to open a browser for the user, but this will not work as expected when viewing your app over a network; it will open a browser on the Streamlit server, unseen by the user.
26 |
1 | ---
2 | title: Caching overview
3 | slug: /develop/concepts/architecture/caching
4 | ---
5 |
6 | # Caching overview
7 |
8 | Streamlit runs your script from top to bottom at every user interaction or code change. This execution model makes development super easy. But it comes with two major challenges:
9 |
10 | 1. Long-running functions run again and again, which slows down your app.
11 | 2. Objects get recreated again and again, which makes it hard to persist them across reruns or sessions.
12 |
13 | But don't worry! Streamlit lets you tackle both issues with its built-in caching mechanism. Caching stores the results of slow function calls, so they only need to run once. This makes your app much faster and helps with persisting objects across reruns. Cached values are available to all users of your app. If you need to save results that should only be accessible within a session, use Session State instead.
14 |
15 |
16 |
17 | 1. Minimal example
18 | 2. Basic usage
19 | 3. Advanced usage
20 | 4. Migrating from st.cache
21 |
22 |
23 |
24 | ## Minimal example
25 |
26 | To cache a function in Streamlit, you must decorate it with one of two decorators (st.cache_data
or st.cache_resource
):
27 |
28 | python 29 | @st.cache_data 30 | def long_running_function(param1, param2): 31 | return … 32 |
33 |
34 | In this example, decorating long_running_function
with @st.cache_data
tells Streamlit that whenever the function is called, it checks two things:
35 |
36 | 1. The values of the input parameters (in this case, param1
and param2
).
37 | 2. The code inside the function.
38 |
39 | If this is the first time Streamlit sees these parameter values and function code, it runs the function and stores the return value in a cache. The next time the function is called with the same parameters and code (e.g., when a user interacts with the app), Streamlit will skip executing the function altogether and return the cached value instead. During development, the cache updates automatically as the function code changes, ensuring that the latest changes are reflected in the cache.
40 |
41 | As mentioned, there are two caching decorators:
42 |
43 | - st.cache_data
is the recommended way to cache computations that return data: loading a DataFrame from CSV, transforming a NumPy array, querying an API, or any other function that returns a serializable data object (str, int, float, DataFrame, array, list, …). It creates a new copy of the data at each function call, making it safe against mutations and race conditions. The behavior of st.cache_data
is what you want in most cases – so if you're unsure, start with st.cache_data
and see if it works!
44 | - st.cache_resource
is the recommended way to cache global resources like ML models or database connections – unserializable objects that you don't want to load multiple times. Using it, you can share these resources across all reruns and sessions of an app without copying or duplication. Note that any mutations to the cached return value directly mutate the object in the cache (more details below).
45 |
46 |
47 |
48 | ## Basic usage
49 |
50 | ### st.cache_data
51 |
52 |
st.cache_data
is your go-to command for all functions that return data – whether DataFrames, NumPy arrays, str, int, float, or other serializable types. It's the right command for almost all use cases! Within each user session, an @st.cache_data
-decorated function returns a copy of the cached return value (if the value is already cached).
53 |
54 | #### Usage
55 |
56 |
57 |
58 | Let's look at an example of using st.cache_data
. Suppose your app loads the Uber ride-sharing dataset – a CSV file of 50 MB – from the internet into a DataFrame:
59 |
60 | python 61 | def load_data(url): 62 | df = pd.read_csv(url) # 👈 Download the data 63 | return df 64 | 65 | df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv") 66 | st.dataframe(df) 67 | 68 | st.button("Rerun") 69 |
70 |
71 | Running the load_data
function takes 2 to 30 seconds, depending on your internet connection. (Tip: if you are on a slow connection, use this 5 MB dataset instead). Without caching, the download is rerun each time the app is loaded or with user interaction. Try it yourself by clicking the button we added! Not a great experience… 😕
72 |
73 | Now let's add the @st.cache_data
decorator on load_data
:
74 |
75 | python 76 | @st.cache_data # 👈 Add the caching decorator 77 | def load_data(url): 78 | df = pd.read_csv(url) 79 | return df 80 | 81 | df = load_data("https://github.com/plotly/datasets/raw/master/uber-rides-data1.csv") 82 | st.dataframe(df) 83 | 84 | st.button("Rerun") 85 |
86 |
87 | Run the app again. You'll notice that the slow download only happens on the first run. Every subsequent rerun should be almost instant! 💨
88 |
89 | #### Behavior
90 |
91 |
92 |
93 | How does this work? Let's go through the behavior of st.cache_data
step by step:
94 |
95 | - On the first run, Streamlit recognizes that it has never called the load_data
function with the specified parameter value (the URL of the CSV file) So it runs the function and downloads the data.
96 | - Now our caching mechanism becomes active: the returned DataFrame is serialized (converted to bytes) via pickle and stored in the cache (together with the value of the url
parameter).
97 | - On the next run, Streamlit checks the cache for an entry of load_data
with the specific url
. There is one! So it retrieves the cached object, deserializes it to a DataFrame, and returns it instead of re-running the function and downloading the data again.
98 |
99 | This process of serializing and deserializing the cached object creates a copy of our original DataFrame. While this copying behavior may seem unnecessary, it's what we want when caching data objects since it effectively prevents mutation and concurrency issues. Read the section “Mutation and concurrency issues" below to understand this in more detail.
100 |
101 |
102 |
103 | st.cache_data
implicitly uses the pickle
module, which is known to be insecure. Anything your cached function returns is pickled and stored, then unpickled on retrieval. Ensure your cached functions return trusted values because it is possible to construct malicious pickle data that will execute arbitrary code during unpickling. Never load data that could have come from an untrusted source in an unsafe mode or that could have been tampered with. Only load data you trust.
104 |
105 |
106 |
107 | #### Examples
108 |
109 |
110 |
111 | DataFrame transformations
112 |
113 | In the example above, we already showed how to cache loading a DataFrame. It can also be useful to cache DataFrame transformations such as df.filter
, df.apply
, or df.sort_values
. Especially with large DataFrames, these operations can be slow.
114 |
115 | python 116 | @st.cache_data 117 | def transform(df): 118 | df = df.filter(items=['one', 'three']) 119 | df = df.apply(np.sum, axis=0) 120 | return df 121 |
122 |
123 | Array computations
124 |
125 | Similarly, it can make sense to cache computations on NumPy arrays:
126 |
127 | python 128 | @st.cache_data 129 | def add(arr1, arr2): 130 | return arr1 + arr2 131 |
132 |
133 | Database queries
134 |
135 | You usually make SQL queries to load data into your app when working with databases. Repeatedly running these queries can be slow, cost money, and degrade the performance of your database. We strongly recommend caching any database queries in your app. See also our guides on connecting Streamlit to different databases for in-depth examples.
136 |
137 | python 138 | connection = database.connect() 139 | 140 | @st.cache_data 141 | def query(): 142 | return pd.read_sql_query("SELECT * from table", connection) 143 |
144 |
145 |
146 |
147 | You should set a ttl
(time to live) to get new results from your database. If you set st.cache_data(ttl=3600)
, Streamlit invalidates any cached values after 1 hour (3600 seconds) and runs the cached function again. See details in Controlling cache size and duration.
148 |
149 |
150 |
151 | API calls
152 |
153 | Similarly, it makes sense to cache API calls. Doing so also avoids rate limits.
154 |
155 | python 156 | @st.cache_data 157 | def api_call(): 158 | response = requests.get('https://jsonplaceholder.typicode.com/posts/1') 159 | return response.json() 160 |
161 |
162 | Running ML models (inference)
163 |
164 | Running complex machine learning models can use significant time and memory. To avoid rerunning the same computations over and over, use caching.
165 |
166 | python 167 | @st.cache_data 168 | def run_model(inputs): 169 | return model(inputs) 170 |
171 |
172 | ### st.cache_resource
173 |
174 | st.cache_resource
is the right command to cache “resources" that should be available globally across all users, sessions, and reruns. It has more limited use cases than st.cache_data
, especially for caching database connections and ML models. Within each user session, an @st.cache_resource
-decorated function returns the cached instance of the return value (if the value is already cached). Therefore, objects cached by st.cache_resource
act like singletons and can mutate.
175 |
176 | #### Usage
177 |
178 | As an example for st.cache_resource
, let's look at a typical machine learning app. As a first step, we need to load an ML model. We do this with Hugging Face's transformers library:
179 |
180 | python 181 | from transformers import pipeline 182 | model = pipeline("sentiment-analysis") # 👈 Load the model 183 |
184 |
185 | If we put this code into a Streamlit app directly, the app will load the model at each rerun or user interaction. Repeatedly loading the model poses two problems:
186 |
187 | - Loading the model takes time and slows down the app.
188 | - Each session loads the model from scratch, which takes up a huge amount of memory.
189 |
190 | Instead, it would make much more sense to load the model once and use that same object across all users and sessions. That's exactly the use case for st.cache_resource
! Let's add it to our app and process some text the user entered:
191 |
192 | python 193 | from transformers import pipeline 194 | 195 | @st.cache_resource # 👈 Add the caching decorator 196 | def load_model(): 197 | return pipeline("sentiment-analysis") 198 | 199 | model = load_model() 200 | 201 | query = st.text_input("Your query", value="I love Streamlit! 🎈") 202 | if query: 203 | result = model(query)[0] # 👈 Classify the query text 204 | st.write(result) 205 |
206 |
207 | If you run this app, you'll see that the app calls load_model
only once – right when the app starts. Subsequent runs will reuse that same model stored in the cache, saving time and memory!
208 |
209 | #### Behavior
210 |
211 |
212 |
213 | Using st.cache_resource
is very similar to using st.cache_data
. But there are a few important differences in behavior:
214 |
215 | - st.cache_resource
does not create a copy of the cached return value but instead stores the object itself in the cache. All mutations on the function's return value directly affect the object in the cache, so you must ensure that mutations from multiple sessions do not cause problems. In short, the return value must be thread-safe.
216 |
217 |
218 |
219 | Using st.cache_resource
on objects that are not thread-safe might lead to crashes or corrupted data. Learn more below under Mutation and concurrency issues.
220 |
221 |
222 | - Not creating a copy means there's just one global instance of the cached return object, which saves memory, e.g. when using a large ML model. In computer science terms, we create a singleton.
223 | - Return values of functions do not need to be serializable. This behavior is great for types not serializable by nature, e.g., database connections, file handles, or threads. Caching these objects with st.cache_data
is not possible.
224 |
225 | #### Examples
226 |
227 |
228 |
229 | Database connections
230 |
231 | st.cache_resource
is useful for connecting to databases. Usually, you're creating a connection object that you want to reuse globally for every query. Creating a new connection object at each run would be inefficient and might lead to connection errors. That's exactly what st.cache_resource
can do, e.g., for a Postgres database:
232 |
233 | python 234 | @st.cache_resource 235 | def init_connection(): 236 | host = "hh-pgsql-public.ebi.ac.uk" 237 | database = "pfmegrnargs" 238 | user = "reader" 239 | password = "NWDMCE5xdipIjRrp" 240 | return psycopg2.connect(host=host, database=database, user=user, password=password) 241 | 242 | conn = init_connection() 243 |
244 |
245 | Of course, you can do the same for any other database. Have a look at our guides on how to connect Streamlit to databases for in-depth examples.
246 |
247 | Loading ML models
248 |
249 | Your app should always cache ML models, so they are not loaded into memory again for every new session. See the example above for how this works with 🤗 Hugging Face models. You can do the same thing for PyTorch, TensorFlow, etc. Here's an example for PyTorch:
250 |
251 | python 252 | @st.cache_resource 253 | def load_model(): 254 | model = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT) 255 | model.eval() 256 | return model 257 | 258 | model = load_model() 259 |
260 |
261 | ### Deciding which caching decorator to use
262 |
263 |
264 |
265 | The sections above showed many common examples for each caching decorator. But there are edge cases for which it's less trivial to decide which caching decorator to use. Eventually, it all comes down to the difference between “data" and “resource":
266 |
267 | - Data are serializable objects (objects that can be converted to bytes via pickle) that you could easily save to disk. Imagine all the types you would usually store in a database or on a file system – basic types like str, int, and float, but also arrays, DataFrames, images, or combinations of these types (lists, tuples, dicts, and so on).
268 | - Resources are unserializable objects that you usually would not save to disk or a database. They are often more complex, non-permanent objects like database connections, ML models, file handles, threads, etc.
269 |
270 | From the types listed above, it should be obvious that most objects in Python are “data." That's also why st.cache_data
is the correct command for almost all use cases. st.cache_resource
is a more exotic command that you should only use in specific situations.
271 |
272 | Or if you're lazy and don't want to think too much, look up your use case or return type in the table below 😉:
273 |
274 | | Use case | Typical return types | Caching decorator |
275 | | :----------------------------------- | -------------------------------------------------------------------------------------------------------------------------: | -----------------------------------------------------------------------------------------------------------------------------------------------------------: |
276 | | Reading a CSV file with pd.read_csv | pandas.DataFrame | st.cache_data |
277 | | Reading a text file | str, list of str | st.cache_data |
278 | | Transforming pandas dataframes | pandas.DataFrame, pandas.Series | st.cache_data |
279 | | Computing with numpy arrays | numpy.ndarray | st.cache_data |
280 | | Simple computations with basic types | str, int, float, … | st.cache_data |
281 | | Querying a database | pandas.DataFrame | st.cache_data |
282 | | Querying an API | pandas.DataFrame, str, dict | st.cache_data |
283 | | Running an ML model (inference) | pandas.DataFrame, str, int, dict, list | st.cache_data |
284 | | Creating or processing images | PIL.Image.Image, numpy.ndarray | st.cache_data |
285 | | Creating charts | matplotlib.figure.Figure, plotly.graph_objects.Figure, altair.Chart | st.cache_data (but some libraries require st.cache_resource, since the chart object is not serializable – make sure not to mutate the chart after creation!) |
286 | | Lazy computations | polars.LazyFrame | st.cache_resource (but may be better to use st.cache_data on the collected results) |
287 | | Loading ML models | transformers.Pipeline, torch.nn.Module, tensorflow.keras.Model | st.cache_resource |
288 | | Initializing database connections | pyodbc.Connection, sqlalchemy.engine.base.Engine, psycopg2.connection, mysql.connector.MySQLConnection, sqlite3.Connection | st.cache_resource |
289 | | Opening persistent file handles | _io.TextIOWrapper | st.cache_resource |
290 | | Opening persistent threads | threading.thread | st.cache_resource |
291 |
292 | ## Advanced usage
293 |
294 | ### Controlling cache size and duration
295 |
296 | If your app runs for a long time and constantly caches functions, you might run into two problems:
297 |
298 | 1. The app runs out of memory because the cache is too large.
299 | 2. Objects in the cache become stale, e.g. because you cached old data from a database.
300 |
301 | You can combat these problems with the ttl
and max_entries
parameters, which are available for both caching decorators.
302 |
303 | The ttl
(time-to-live) parameter
304 |
305 | ttl
sets a time to live on a cached function. If that time is up and you call the function again, the app will discard any old, cached values, and the function will be rerun. The newly computed value will then be stored in the cache. This behavior is useful for preventing stale data (problem 2) and the cache from growing too large (problem 1). Especially when pulling data from a database or API, you should always set a ttl
so you are not using old data. Here's an example:
306 |
307 | python 308 | @st.cache_data(ttl=3600) # 👈 Cache data for 1 hour (=3600 seconds) 309 | def get_api_data(): 310 | data = api.get(...) 311 | return data 312 |
313 |
314 |
315 |
316 | You can also set ttl
values using timedelta
, e.g., ttl=datetime.timedelta(hours=1)
.
317 |
318 |
319 |
320 | The max_entries
parameter
321 |
322 | max_entries
sets the maximum number of entries in the cache. An upper bound on the number of cache entries is useful for limiting memory (problem 1), especially when caching large objects. The oldest entry will be removed when a new entry is added to a full cache. Here's an example:
323 |
324 | python 325 | @st.cache_data(max_entries=1000) # 👈 Maximum 1000 entries in the cache 326 | def get_large_array(seed): 327 | np.random.seed(seed) 328 | arr = np.random.rand(100000) 329 | return arr 330 |
331 |
332 | ### Customizing the spinner
333 |
334 | By default, Streamlit shows a small loading spinner in the app when a cached function is running. You can modify it easily with the show_spinner
parameter, which is available for both caching decorators:
335 |
336 | python 337 | @st.cache_data(show_spinner=False) # 👈 Disable the spinner 338 | def get_api_data(): 339 | data = api.get(...) 340 | return data 341 | 342 | @st.cache_data(show_spinner="Fetching data from API...") # 👈 Use custom text for spinner 343 | def get_api_data(): 344 | data = api.get(...) 345 | return data 346 |
347 |
348 | ### Excluding input parameters
349 |
350 | In a cached function, all input parameters must be hashable. Let's quickly explain why and what it means. When the function is called, Streamlit looks at its parameter values to determine if it was cached before. Therefore, it needs a reliable way to compare the parameter values across function calls. Trivial for a string or int – but complex for arbitrary objects! Streamlit uses hashing to solve that. It converts the parameter to a stable key and stores that key. At the next function call, it hashes the parameter again and compares it with the stored hash key.
351 |
352 | Unfortunately, not all parameters are hashable! E.g., you might pass an unhashable database connection or ML model to your cached function. In this case, you can exclude input parameters from caching. Simply prepend the parameter name with an underscore (e.g., _param1
), and it will not be used for caching. Even if it changes, Streamlit will return a cached result if all the other parameters match up.
353 |
354 | Here's an example:
355 |
356 | python 357 | @st.cache_data 358 | def fetch_data(_db_connection, num_rows): # 👈 Don't hash _db_connection 359 | data = _db_connection.fetch(num_rows) 360 | return data 361 | 362 | connection = init_connection() 363 | fetch_data(connection, 10) 364 |
365 |
366 | But what if you want to cache a function that takes an unhashable parameter? For example, you might want to cache a function that takes an ML model as input and returns the layer names of that model. Since the model is the only input parameter, you cannot exclude it from caching. In this case you can use the hash_funcs
parameter to specify a custom hashing function for the model.
367 |
368 | ### The hash_funcs
parameter
369 |
370 | As described above, Streamlit's caching decorators hash the input parameters and cached function's signature to determine whether the function has been run before and has a return value stored ("cache hit") or needs to be run ("cache miss"). Input parameters that are not hashable by Streamlit's hashing implementation can be ignored by prepending an underscore to their name. But there two rare cases where this is undesirable. i.e. where you want to hash the parameter that Streamlit is unable to hash:
371 |
372 | 1. When Streamlit's hashing mechanism fails to hash a parameter, resulting in a UnhashableParamError
being raised.
373 | 2. When you want to override Streamlit's default hashing mechanism for a parameter.
374 |
375 | Let's discuss each of these cases in turn with examples.
376 |
377 | #### Example 1: Hashing a custom class
378 |
379 | Streamlit does not know how to hash custom classes. If you pass a custom class to a cached function, Streamlit will raise a UnhashableParamError
. For example, let's define a custom class MyCustomClass
that accepts an initial integer score. Let's also define a cached function multiply_score
that multiplies the score by a multiplier:
380 |
381 | python 382 | import streamlit as st 383 | 384 | class MyCustomClass: 385 | def __init__(self, initial_score: int): 386 | self.my_score = initial_score 387 | 388 | @st.cache_data 389 | def multiply_score(obj: MyCustomClass, multiplier: int) -> int: 390 | return obj.my_score * multiplier 391 | 392 | initial_score = st.number_input("Enter initial score", value=15) 393 | 394 | score = MyCustomClass(initial_score) 395 | multiplier = 2 396 | 397 | st.write(multiply_score(score, multiplier)) 398 |
399 |
400 | If you run this app, you'll see that Streamlit raises a UnhashableParamError
since it does not know how to hash MyCustomClass
:
401 |
402 | python 403 | UnhashableParamError: Cannot hash argument 'obj' (of type __main__.MyCustomClass) in 'multiply_score'. 404 |
405 |
406 | To fix this, we can use the hash_funcs
parameter to tell Streamlit how to hash MyCustomClass
. We do this by passing a dictionary to hash_funcs
that maps the name of the parameter to a hash function. The choice of hash function is up to the developer. In this case, let's define a custom hash function hash_func
that takes the custom class as input and returns the score. We want the score to be the unique identifier of the object, so we can use it to deterministically hash the object:
407 |
408 | python 409 | import streamlit as st 410 | 411 | class MyCustomClass: 412 | def __init__(self, initial_score: int): 413 | self.my_score = initial_score 414 | 415 | def hash_func(obj: MyCustomClass) -> int: 416 | return obj.my_score # or any other value that uniquely identifies the object 417 | 418 | @st.cache_data(hash_funcs={MyCustomClass: hash_func}) 419 | def multiply_score(obj: MyCustomClass, multiplier: int) -> int: 420 | return obj.my_score * multiplier 421 | 422 | initial_score = st.number_input("Enter initial score", value=15) 423 | 424 | score = MyCustomClass(initial_score) 425 | multiplier = 2 426 | 427 | st.write(multiply_score(score, multiplier)) 428 |
429 |
430 | Now if you run the app, you'll see that Streamlit no longer raises a UnhashableParamError
and the app runs as expected.
431 |
432 | Let's now consider the case where multiply_score
is an attribute of MyCustomClass
and we want to hash the entire object:
433 |
434 | python 435 | import streamlit as st 436 | 437 | class MyCustomClass: 438 | def __init__(self, initial_score: int): 439 | self.my_score = initial_score 440 | 441 | @st.cache_data 442 | def multiply_score(self, multiplier: int) -> int: 443 | return self.my_score * multiplier 444 | 445 | initial_score = st.number_input("Enter initial score", value=15) 446 | 447 | score = MyCustomClass(initial_score) 448 | multiplier = 2 449 | 450 | st.write(score.multiply_score(multiplier)) 451 |
452 |
453 | If you run this app, you'll see that Streamlit raises a UnhashableParamError
since it cannot hash the argument 'self' (of type __main__.MyCustomClass) in 'multiply_score'
. A simple fix here could be to use Python's hash()
function to hash the object:
454 |
455 | python 456 | import streamlit as st 457 | 458 | class MyCustomClass: 459 | def __init__(self, initial_score: int): 460 | self.my_score = initial_score 461 | 462 | @st.cache_data(hash_funcs={"__main__.MyCustomClass": lambda x: hash(x.my_score)}) 463 | def multiply_score(self, multiplier: int) -> int: 464 | return self.my_score * multiplier 465 | 466 | initial_score = st.number_input("Enter initial score", value=15) 467 | 468 | score = MyCustomClass(initial_score) 469 | multiplier = 2 470 | 471 | st.write(score.multiply_score(multiplier)) 472 |
473 |
474 | Above, the hash function is defined as lambda x: hash(x.my_score)
. This creates a hash based on the my_score
attribute of the MyCustomClass
instance. As long as my_score
remains the same, the hash remains the same. Thus, the result of multiply_score
can be retrieved from the cache without recomputation.
475 |
476 | As an astute Pythonista, you may have been tempted to use Python's id()
function to hash the object like so:
477 |
478 | python 479 | import streamlit as st 480 | 481 | class MyCustomClass: 482 | def __init__(self, initial_score: int): 483 | self.my_score = initial_score 484 | 485 | @st.cache_data(hash_funcs={"__main__.MyCustomClass": id}) 486 | def multiply_score(self, multiplier: int) -> int: 487 | return self.my_score * multiplier 488 | 489 | initial_score = st.number_input("Enter initial score", value=15) 490 | 491 | score = MyCustomClass(initial_score) 492 | multiplier = 2 493 | 494 | st.write(score.multiply_score(multiplier)) 495 |
496 |
497 | If you run the app, you'll notice that Streamlit recomputes multiply_score
each time even if my_score
hasn't changed! Puzzled? In Python, id()
returns the identity of an object, which is unique and constant for the object during its lifetime. This means that even if the my_score
value is the same between two instances of MyCustomClass
, id()
will return different values for these two instances, leading to different hash values. As a result, Streamlit considers these two different instances as needing separate cached values, thus it recomputes the multiply_score
each time even if my_score
hasn't changed.
498 |
499 | This is why we discourage using it as hash func, and instead encourage functions that return deterministic, true hash values. That said, if you know what you're doing, you can use id()
as a hash function. Just be aware of the consequences. For example, id
is often the correct hash func when you're passing the result of an @st.cache_resource
function as the input param to another cached function. There's a whole class of object types that aren’t otherwise hashable.
500 |
501 | #### Example 2: Hashing a Pydantic model
502 |
503 | Let's consider another example where we want to hash a Pydantic model:
504 |
505 | python 506 | import streamlit as st 507 | from pydantic import BaseModel 508 | 509 | class Person(BaseModel): 510 | name: str 511 | 512 | @st.cache_data 513 | def identity(person: Person): 514 | return person 515 | 516 | person = identity(Person(name="Lee")) 517 | st.write(f"The person is {person.name}") 518 |
519 |
520 | Above, we define a custom class Person
using Pydantic's BaseModel
with a single attribute name. We also define an identity
function which accepts an instance of Person
as an arg and returns it without modification. This function is intended to cache the result, therefore, if called multiple times with the same Person
instance, it won't recompute but return the cached instance.
521 |
522 | If you run the app, however, you'll run into a UnhashableParamError: Cannot hash argument 'person' (of type __main__.Person) in 'identity'.
error. This is because Streamlit does not know how to hash the Person
class. To fix this, we can use the hash_funcs
kwarg to tell Streamlit how to hash Person
.
523 |
524 | In the version below, we define a custom hash function hash_func
that takes the Person
instance as input and returns the name attribute. We want the name to be the unique identifier of the object, so we can use it to deterministically hash the object:
525 |
526 | python 527 | import streamlit as st 528 | from pydantic import BaseModel 529 | 530 | class Person(BaseModel): 531 | name: str 532 | 533 | @st.cache_data(hash_funcs={Person: lambda p: p.name}) 534 | def identity(person: Person): 535 | return person 536 | 537 | person = identity(Person(name="Lee")) 538 | st.write(f"The person is {person.name}") 539 |
540 |
541 | #### Example 3: Hashing a ML model
542 |
543 | There may be cases where you want to pass your favorite machine learning model to a cached function. For example, let's say you want to pass a TensorFlow model to a cached function, based on what model the user selects in the app. You might try something like this:
544 |
545 | python 546 | import streamlit as st 547 | import tensorflow as tf 548 | 549 | @st.cache_resource 550 | def load_base_model(option): 551 | if option == 1: 552 | return tf.keras.applications.ResNet50(include_top=False, weights="imagenet") 553 | else: 554 | return tf.keras.applications.MobileNetV2(include_top=False, weights="imagenet") 555 | 556 | @st.cache_resource 557 | def load_layers(base_model): 558 | return [layer.name for layer in base_model.layers] 559 | 560 | option = st.radio("Model 1 or 2", [1, 2]) 561 | 562 | base_model = load_base_model(option) 563 | 564 | layers = load_layers(base_model) 565 | 566 | st.write(layers) 567 |
568 |
569 | In the above app, the user can select one of two models. Based on the selection, the app loads the corresponding model and passes it to load_layers
. This function then returns the names of the layers in the model. If you run the app, you'll see that Streamlit raises a UnhashableParamError
since it cannot hash the argument 'base_model' (of type keras.engine.functional.Functional) in 'load_layers'
.
570 |
571 | If you disable hashing for base_model
by prepending an underscore to its name, you'll observe that regardless of which base model is chosen, the layers displayed are same. This subtle bug is due to the fact that the load_layers
function is not re-run when the base model changes. This is because Streamlit does not hash the base_model
argument, so it does not know that the function needs to be re-run when the base model changes.
572 |
573 | To fix this, we can use the hash_funcs
kwarg to tell Streamlit how to hash the base_model
argument. In the version below, we define a custom hash function hash_func
: Functional: lambda x: x.name
. Our choice of hash func is informed by our knowledge that the name
attribute of a Functional
object or model uniquely identifies it. As long as the name
attribute remains the same, the hash remains the same. Thus, the result of load_layers
can be retrieved from the cache without recomputation.
574 |
575 | python 576 | import streamlit as st 577 | import tensorflow as tf 578 | from keras.engine.functional import Functional 579 | 580 | @st.cache_resource 581 | def load_base_model(option): 582 | if option == 1: 583 | return tf.keras.applications.ResNet50(include_top=False, weights="imagenet") 584 | else: 585 | return tf.keras.applications.MobileNetV2(include_top=False, weights="imagenet") 586 | 587 | @st.cache_resource(hash_funcs={Functional: lambda x: x.name}) 588 | def load_layers(base_model): 589 | return [layer.name for layer in base_model.layers] 590 | 591 | option = st.radio("Model 1 or 2", [1, 2]) 592 | 593 | base_model = load_base_model(option) 594 | 595 | layers = load_layers(base_model) 596 | 597 | st.write(layers) 598 |
599 |
600 | In the above case, we could also have used hash_funcs={Functional: id}
as the hash function. This is because id
is often the correct hash func when you're passing the result of an @st.cache_resource
function as the input param to another cached function.
601 |
602 | #### Example 4: Overriding Streamlit's default hashing mechanism
603 |
604 | Let's consider another example where we want to override Streamlit's default hashing mechanism for a pytz-localized datetime object:
605 |
606 | python 607 | from datetime import datetime 608 | import pytz 609 | import streamlit as st 610 | 611 | tz = pytz.timezone("Europe/Berlin") 612 | 613 | @st.cache_data 614 | def load_data(dt): 615 | return dt 616 | 617 | now = datetime.now() 618 | st.text(load_data(dt=now)) 619 | 620 | now_tz = tz.localize(datetime.now()) 621 | st.text(load_data(dt=now_tz)) 622 |
623 |
624 | It may be surprising to see that although now
and now_tz
are of the same <class 'datetime.datetime'>
type, Streamlit does not how to hash now_tz
and raises a UnhashableParamError
. In this case, we can override Streamlit's default hashing mechanism for datetime
objects by passing a custom hash function to the hash_funcs
kwarg:
625 |
626 | python 627 | from datetime import datetime 628 | 629 | import pytz 630 | import streamlit as st 631 | 632 | tz = pytz.timezone("Europe/Berlin") 633 | 634 | @st.cache_data(hash_funcs={datetime: lambda x: x.strftime("%a %d %b %Y, %I:%M%p")}) 635 | def load_data(dt): 636 | return dt 637 | 638 | now = datetime.now() 639 | st.text(load_data(dt=now)) 640 | 641 | now_tz = tz.localize(datetime.now()) 642 | st.text(load_data(dt=now_tz)) 643 |
644 |
645 | Let's now consider a case where we want to override Streamlit's default hashing mechanism for NumPy arrays. While Streamlit natively hashes Pandas and NumPy objects, there may be cases where you want to override Streamlit's default hashing mechanism for these objects.
646 |
647 | For example, let's say we create a cache-decorated show_data
function that accepts a NumPy array and returns it without modification. In the bellow app, data = df["str"].unique()
(which is a NumPy array) is passed to the show_data
function.
648 |
649 | python 650 | import time 651 | import numpy as np 652 | import pandas as pd 653 | import streamlit as st 654 | 655 | @st.cache_data 656 | def get_data(): 657 | df = pd.DataFrame({"num": [112, 112, 2, 3], "str": ["be", "a", "be", "c"]}) 658 | return df 659 | 660 | @st.cache_data 661 | def show_data(data): 662 | time.sleep(2) # This makes the function take 2s to run 663 | return data 664 | 665 | df = get_data() 666 | data = df["str"].unique() 667 | 668 | st.dataframe(show_data(data)) 669 | st.button("Re-run") 670 |
671 |
672 | Since data
is always the same, we expect the show_data
function to return the cached value. However, if you run the app, and click the Re-run
button, you'll notice that the show_data
function is re-run each time. We can assume this behavior is a consequence of Streamlit's default hashing mechanism for NumPy arrays.
673 |
674 | To work around this, let's define a custom hash function hash_func
that takes a NumPy array as input and returns a string representation of the array:
675 |
676 | python 677 | import time 678 | import numpy as np 679 | import pandas as pd 680 | import streamlit as st 681 | 682 | @st.cache_data 683 | def get_data(): 684 | df = pd.DataFrame({"num": [112, 112, 2, 3], "str": ["be", "a", "be", "c"]}) 685 | return df 686 | 687 | @st.cache_data(hash_funcs={np.ndarray: str}) 688 | def show_data(data): 689 | time.sleep(2) # This makes the function take 2s to run 690 | return data 691 | 692 | df = get_data() 693 | data = df["str"].unique() 694 | 695 | st.dataframe(show_data(data)) 696 | st.button("Re-run") 697 |
698 |
699 | Now if you run the app, and click the Re-run
button, you'll notice that the show_data
function is no longer re-run each time. It's important to note here that our choice of hash function was very naive and not necessarily the best choice. For example, if the NumPy array is large, converting it to a string representation may be expensive. In such cases, it is up to you as the developer to define what a good hash function is for your use case.
700 |
701 | #### Static elements
702 |
703 | Since version 1.16.0, cached functions can contain Streamlit commands! For example, you can do this:
704 |
705 | python 706 | @st.cache_data 707 | def get_api_data(): 708 | data = api.get(...) 709 | st.success("Fetched data from API!") # 👈 Show a success message 710 | return data 711 |
712 |
713 | As we know, Streamlit only runs this function if it hasn't been cached before. On this first run, the st.success
message will appear in the app. But what happens on subsequent runs? It still shows up! Streamlit realizes that there is an st.
command inside the cached function, saves it during the first run, and replays it on subsequent runs. Replaying static elements works for both caching decorators.
714 |
715 | You can also use this functionality to cache entire parts of your UI:
716 |
717 | python 718 | @st.cache_data 719 | def show_data(): 720 | st.header("Data analysis") 721 | data = api.get(...) 722 | st.success("Fetched data from API!") 723 | st.write("Here is a plot of the data:") 724 | st.line_chart(data) 725 | st.write("And here is the raw data:") 726 | st.dataframe(data) 727 |
728 |
729 | #### Input widgets
730 |
731 | You can also use interactive input widgets like st.slider
or st.text_input
in cached functions. Widget replay is an experimental feature at the moment. To enable it, you need to set the experimental_allow_widgets
parameter:
732 |
733 | python 734 | @st.cache_data(experimental_allow_widgets=True) # 👈 Set the parameter 735 | def get_data(): 736 | num_rows = st.slider("Number of rows to get") # 👈 Add a slider 737 | data = api.get(..., num_rows) 738 | return data 739 |
740 |
741 | Streamlit treats the slider like an additional input parameter to the cached function. If you change the slider position, Streamlit will see if it has already cached the function for this slider value. If yes, it will return the cached value. If not, it will rerun the function using the new slider value.
742 |
743 | Using widgets in cached functions is extremely powerful because it lets you cache entire parts of your app. But it can be dangerous! Since Streamlit treats the widget value as an additional input parameter, it can easily lead to excessive memory usage. Imagine your cached function has five sliders and returns a 100 MB DataFrame. Then we'll add 100 MB to the cache for every permutation of these five slider values – even if the sliders do not influence the returned data! These additions can make your cache explode very quickly. Please be aware of this limitation if you use widgets in cached functions. We recommend using this feature only for isolated parts of your UI where the widgets directly influence the cached return value.
744 |
745 |
746 |
747 | Support for widgets in cached functions is experimental. We may change or remove it anytime without warning. Please use it with care!
748 |
749 |
750 |
751 |
752 | Two widgets are currently not supported in cached functions: st.file_uploader
and st.camera_input
. We may support them in the future. Feel free to open a GitHub issue if you need them!
753 |
754 |
755 | ### Dealing with large data
756 |
757 | As we explained, you should cache data objects with st.cache_data
. But this can be slow for extremely large data, e.g., DataFrames or arrays with >100 million rows. That's because of the copying behavior of st.cache_data
: on the first run, it serializes the return value to bytes and deserializes it on subsequent runs. Both operations take time.
758 |
759 | If you're dealing with extremely large data, it can make sense to use st.cache_resource
instead. It does not create a copy of the return value via serialization/deserialization and is almost instant. But watch out: any mutation to the function's return value (such as dropping a column from a DataFrame or setting a value in an array) directly manipulates the object in the cache. You must ensure this doesn't corrupt your data or lead to crashes. See the section on Mutation and concurrency issues below.
760 |
761 | When benchmarking st.cache_data
on pandas DataFrames with four columns, we found that it becomes slow when going beyond 100 million rows. The table shows runtimes for both caching decorators at different numbers of rows (all with four columns):
762 |
763 | | | | 10M rows | 50M rows | 100M rows | 200M rows |
764 | | ----------------- | --------------- | :------: | :------: | :-------: | :-------: |
765 | | st.cache_data | First run* | 0.4 s | 3 s | 14 s | 28 s |
766 | | | Subsequent runs | 0.2 s | 1 s | 2 s | 7 s |
767 | | st.cache_resource | First run* | 0.01 s | 0.1 s | 0.2 s | 1 s |
768 | | | Subsequent runs | 0 s | 0 s | 0 s | 0 s |
769 |
770 | | |
771 | | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
772 | | *For the first run, the table only shows the overhead time of using the caching decorator. It does not include the runtime of the cached function itself. |
773 |
774 | ### Mutation and concurrency issues
775 |
776 | In the sections above, we talked a lot about issues when mutating return objects of cached functions. This topic is complicated! But it's central to understanding the behavior differences between st.cache_data
and st.cache_resource
. So let's dive in a bit deeper.
777 |
778 | First, we should clearly define what we mean by mutations and concurrency:
779 |
780 | - By mutations, we mean any changes made to a cached function's return value after that function has been called. I.e. something like this:
781 |
782 | python 783 | @st.cache_data 784 | def create_list(): 785 | l = [1, 2, 3] 786 | 787 | l = create_list() # 👈 Call the function 788 | l[0] = 2 # 👈 Mutate its return value 789 |
790 |
791 | - By concurrency, we mean that multiple sessions can cause these mutations at the same time. Streamlit is a web framework that needs to handle many users and sessions connecting to an app. If two people view an app at the same time, they will both cause the Python script to rerun, which may manipulate cached return objects at the same time – concurrently.
792 |
793 | Mutating cached return objects can be dangerous. It can lead to exceptions in your app and even corrupt your data (which can be worse than a crashed app!). Below, we'll first explain the copying behavior of st.cache_data
and show how it can avoid mutation issues. Then, we'll show how concurrent mutations can lead to data corruption and how to prevent it.
794 |
795 | #### Copying behavior
796 |
797 | st.cache_data
creates a copy of the cached return value each time the function is called. This avoids most mutations and concurrency issues. To understand it in detail, let's go back to the Uber ridesharing example from the section on st.cache_data
above. We are making two modifications to it:
798 |
799 | 1. We are using st.cache_resource
instead of st.cache_data
. st.cache_resource
does not create a copy of the cached object, so we can see what happens without the copying behavior.
800 | 2. After loading the data, we manipulate the returned DataFrame (in place!) by dropping the column "Lat"
.
801 |
802 | Here's the code:
803 |
804 | python 805 | @st.cache_resource # 👈 Turn off copying behavior 806 | def load_data(url): 807 | df = pd.read_csv(url) 808 | return df 809 | 810 | df = load_data("https://raw.githubusercontent.com/plotly/datasets/master/uber-rides-data1.csv") 811 | st.dataframe(df) 812 | 813 | df.drop(columns=['Lat'], inplace=True) # 👈 Mutate the dataframe inplace 814 | 815 | st.button("Rerun") 816 |
817 |
818 | Let's run it and see what happens! The first run should work fine. But in the second run, you see an exception: KeyError: "['Lat'] not found in axis"
. Why is that happening? Let's go step by step:
819 |
820 | - On the first run, Streamlit runs load_data
and stores the resulting DataFrame in the cache. Since we're using st.cache_resource
, it does not create a copy but stores the original DataFrame.
821 | - Then we drop the column "Lat"
from the DataFrame. Note that this is dropping the column from the original DataFrame stored in the cache. We are manipulating it!
822 | - On the second run, Streamlit returns that exact same manipulated DataFrame from the cache. It does not have the column "Lat"
anymore! So our call to df.drop
results in an exception. Pandas cannot drop a column that doesn't exist.
823 |
824 | The copying behavior of st.cache_data
prevents this kind of mutation error. Mutations can only affect a specific copy and not the underlying object in the cache. The next rerun will get its own, unmutated copy of the DataFrame. You can try it yourself, just replace st.cache_resource
with st.cache_data
above, and you'll see that everything works.
825 |
826 | Because of this copying behavior, st.cache_data
is the recommended way to cache data transforms and computations – anything that returns a serializable object.
827 |
828 | #### Concurrency issues
829 |
830 | Now let's look at what can happen when multiple users concurrently mutate an object in the cache. Let's say you have a function that returns a list. Again, we are using st.cache_resource
to cache it so that we are not creating a copy:
831 |
832 | python 833 | @st.cache_resource 834 | def create_list(): 835 | l = [1, 2, 3] 836 | return l 837 | 838 | l = create_list() 839 | first_list_value = l[0] 840 | l[0] = first_list_value + 1 841 | 842 | st.write("l[0] is:", l[0]) 843 |
844 |
845 | Let's say user A runs the app. They will see the following output:
846 |
847 | python 848 | l[0] is: 2 849 |
850 |
851 | Let's say another user, B, visits the app right after. In contrast to user A, they will see the following output:
852 |
853 | python 854 | l[0] is: 3 855 |
856 |
857 | Now, user A reruns the app immediately after user B. They will see the following output:
858 |
859 | python 860 | l[0] is: 4 861 |
862 |
863 | What is happening here? Why are all outputs different?
864 |
865 | - When user A visits the app, create_list()
is called, and the list [1, 2, 3]
is stored in the cache. This list is then returned to user A. The first value of the list, 1
, is assigned to first_list_value
, and l[0]
is changed to 2
.
866 | - When user B visits the app, create_list()
returns the mutated list from the cache: [2, 2, 3]
. The first value of the list, 2
, is assigned to first_list_value
and l[0]
is changed to 3
.
867 | - When user A reruns the app, create_list()
returns the mutated list again: [3, 2, 3]
. The first value of the list, 3
, is assigned to first_list_value,
and l[0]
is changed to 4.
868 |
869 | If you think about it, this makes sense. Users A and B use the same list object (the one stored in the cache). And since the list object is mutated, user A's change to the list object is also reflected in user B's app.
870 |
871 | This is why you must be careful about mutating objects cached with st.cache_resource
, especially when multiple users access the app concurrently. If we had used st.cache_data
instead of st.cache_resource
, the app would have copied the list object for each user, and the above example would have worked as expected – users A and B would have both seen:
872 |
873 | python 874 | l[0] is: 2 875 |
876 |
877 |
878 |
879 | This toy example might seem benign. But data corruption can be extremely dangerous! Imagine we had worked with the financial records of a large bank here. You surely don't want to wake up with less money on your account just because someone used the wrong caching decorator 😉
880 |
881 |
882 |
883 | ## Migrating from st.cache
884 |
885 | We introduced the caching commands described above in Streamlit 1.18.0. Before that, we had one catch-all command st.cache
. Using it was often confusing, resulted in weird exceptions, and was slow. That's why we replaced st.cache
with the new commands in 1.18.0 (read more in this blog post). The new commands provide a more intuitive and efficient way to cache your data and resources and are intended to replace st.cache
in all new development.
886 |
887 | If your app is still using st.cache
, don't despair! Here are a few notes on migrating:
888 |
889 | - Streamlit will show a deprecation warning if your app uses st.cache
.
890 | - We will not remove st.cache
soon, so you don't need to worry about your 2-year-old app breaking. But we encourage you to try the new commands going forward – they will be way less annoying!
891 | - Switching code to the new commands should be easy in most cases. To decide whether to use st.cache_data
or st.cache_resource
, read Deciding which caching decorator to use. Streamlit will also recognize common use cases and show hints right in the deprecation warnings.
892 | - Most parameters from st.cache
are also present in the new commands, with a few exceptions:
893 | - allow_output_mutation
does not exist anymore. You can safely delete it. Just make sure you use the right caching command for your use case.
894 | - suppress_st_warning
does not exist anymore. You can safely delete it. Cached functions can now contain Streamlit commands and will replay them. If you want to use widgets inside cached functions, set experimental_allow_widgets=True
. See Input widgets for an example.
895 |
896 | If you have any questions or issues during the migration process, please contact us on the forum, and we will be happy to assist you. 🎈
897 |
1 | ---
2 | title: Using forms
3 | slug: /develop/concepts/architecture/forms
4 | ---
5 |
6 | # Using forms
7 |
8 | When you don't want to rerun your script with each input made by a user, st.form
is here to help! Forms make it easy to batch user input into a single rerun. This guide to using forms provides examples and explains how users interact with forms.
9 |
10 | ## Example
11 |
12 | In the following example, a user can set multiple parameters to update the map. As the user changes the parameters, the script will not rerun and the map will not update. When the user submits the form with the button labeled "Update map", the script reruns and the map updates.
13 |
14 | If at any time the user clicks "Generate new points" which is outside of the form, the script will rerun. If the user has any unsubmitted changes within the form, these will not be sent with the rerun. All changes made to a form will only be sent to the Python backend when the form itself is submitted.
15 |
16 |
17 |
18 | python 19 | import streamlit as st 20 | import pandas as pd 21 | import numpy as np 22 | 23 | def get_data(): 24 | df = pd.DataFrame({ 25 | "lat": np.random.randn(200) / 50 + 37.76, 26 | "lon": np.random.randn(200) / 50 + -122.4, 27 | "team": ['A','B']*100 28 | }) 29 | return df 30 | 31 | if st.button('Generate new points'): 32 | st.session_state.df = get_data() 33 | if 'df' not in st.session_state: 34 | st.session_state.df = get_data() 35 | df = st.session_state.df 36 | 37 | with st.form("my_form"): 38 | header = st.columns([1,2,2]) 39 | header[0].subheader('Color') 40 | header[1].subheader('Opacity') 41 | header[2].subheader('Size') 42 | 43 | row1 = st.columns([1,2,2]) 44 | colorA = row1[0].color_picker('Team A', '#0000FF') 45 | opacityA = row1[1].slider('A opacity', 20, 100, 50, label_visibility='hidden') 46 | sizeA = row1[2].slider('A size', 50, 200, 100, step=10, label_visibility='hidden') 47 | 48 | row2 = st.columns([1,2,2]) 49 | colorB = row2[0].color_picker('Team B', '#FF0000') 50 | opacityB = row2[1].slider('B opacity', 20, 100, 50, label_visibility='hidden') 51 | sizeB = row2[2].slider('B size', 50, 200, 100, step=10, label_visibility='hidden') 52 | 53 | st.form_submit_button('Update map') 54 | 55 | alphaA = int(opacityA*255/100) 56 | alphaB = int(opacityB*255/100) 57 | 58 | df['color'] = np.where(df.team=='A',colorA+f'{alphaA:02x}',colorB+f'{alphaB:02x}') 59 | df['size'] = np.where(df.team=='A',sizeA, sizeB) 60 | 61 | st.map(df, size='size', color='color') 62 |
63 |
64 |
65 |
66 |
67 |
68 | ## User interaction
69 |
70 | If a widget is not in a form, that widget will trigger a script rerun whenever a user changes its value. For widgets with keyed input (st.number_input
, st.text_input
, st.text_area
), a new value triggers a rerun when the user clicks or tabs out of the widget. A user can also submit a change by pressing Enter
while their cursor is active in the widget.
71 |
72 | On the other hand if a widget is inside of a form, the script will not rerun when a user clicks or tabs out of that widget. For widgets inside a form, the script will rerun when the form is submitted and all widgets within the form will send their updated values to the Python backend.
73 |
74 |
75 |
76 | A user can submit a form using Enter on their keyboard if their cursor active in a widget that accepts keyed input. Within
st.number_input
and st.text_input
a user presses Enter to submit the form. Within st.text_area
a user presses Ctrl+Enter/⌘+Enter to submit the form.
77 |
78 |
79 |
80 | ## Widget values
81 |
82 | Before a form is submitted, all widgets within that form will have default values, just like widgets outside of a form have default values.
83 |
84 |
python 85 | import streamlit as st 86 | 87 | with st.form("my_form"): 88 | st.write("Inside the form") 89 | my_number = st.slider('Pick a number', 1, 10) 90 | my_color = st.selectbox('Pick a color', ['red','orange','green','blue','violet']) 91 | st.form_submit_button('Submit my picks') 92 | 93 | # This is outside the form 94 | st.write(my_number) 95 | st.write(my_color) 96 |
97 |
98 |
99 |
100 | ## Forms are containers
101 |
102 | When st.form
is called, a container is created on the frontend. You can write to that container like you do with other container elements. That is, you can use Python's with
statement as shown in the example above, or you can assign the form container to a variable and call methods on it directly. Additionally, you can place st.form_submit_button
anywhere in the form container.
103 |
104 | python 105 | import streamlit as st 106 | 107 | animal = st.form('my_animal') 108 | 109 | # This is writing directly to the main body. Since the form container is 110 | # defined above, this will appear below everything written in the form. 111 | sound = st.selectbox('Sounds like', ['meow','woof','squeak','tweet']) 112 | 113 | # These methods called on the form container, so they appear inside the form. 114 | submit = animal.form_submit_button(f'Say it with {sound}!') 115 | sentence = animal.text_input('Your sentence:', 'Where\'s the tuna?') 116 | say_it = sentence.rstrip('.,!?') + f', {sound}!' 117 | if submit: 118 | animal.subheader(say_it) 119 | else: 120 | animal.subheader(' ') 121 |
122 |
123 |
124 |
125 | ## Processing form submissions
126 |
127 | The purpose of a form is to override the default behavior of Streamlit which reruns a script as soon as the user makes a change. For widgets outside of a form, the logical flow is:
128 |
129 | 1. The user changes a widget's value on the frontend.
130 | 2. The widget's value in st.session_state
and in the Python backend (server) is updated.
131 | 3. The script rerun begins.
132 | 4. If the widget has a callback, it is executed as a prefix to the page rerun.
133 | 5. When the updated widget's function is executed during the rerun, it outputs the new value.
134 |
135 | For widgets inside a form, any changes made by a user (step 1) do not get passed to the Python backend (step 2) until the form is submitted. Furthermore, the only widget inside a form that can have a callback function is the st.form_submit_button
. If you need to execute a process using newly submitted values, you have three major patterns for doing so.
136 |
137 | ### Execute the process after the form
138 |
139 | If you need to execute a one-time process as a result of a form submission, you can condition that process on the st.form_submit_button
and execute it after the form. If you need results from your process to display above the form, you can use containers to control where the form displays relative to your output.
140 |
141 | python 142 | import streamlit as st 143 | 144 | col1,col2 = st.columns([1,2]) 145 | col1.title('Sum:') 146 | 147 | with st.form('addition'): 148 | a = st.number_input('a') 149 | b = st.number_input('b') 150 | submit = st.form_submit_button('add') 151 | 152 | if submit: 153 | col2.title(f'{a+b:.2f}') 154 |
155 |
156 |
157 |
158 | ### Use a callback with session state
159 |
160 | You can use a callback to execute a process as a prefix to the script rerunning.
161 |
162 |
163 |
164 | When processing newly updated values within a callback, do not pass those values to the callback directly through the args
or kwargs
parameters. You need to assign a key to any widget whose value you use within the callback. If you look up the value of that widget from st.session_state
within the body of the callback, you will be able to access the newly submitted value. See the example below.
165 |
166 |
167 |
168 | python 169 | import streamlit as st 170 | 171 | if 'sum' not in st.session_state: 172 | st.session_state.sum = '' 173 | 174 | def sum(): 175 | result = st.session_state.a + st.session_state.b 176 | st.session_state.sum = result 177 | 178 | col1,col2 = st.columns(2) 179 | col1.title('Sum:') 180 | if isinstance(st.session_state.sum, float): 181 | col2.title(f'{st.session_state.sum:.2f}') 182 | 183 | with st.form('addition'): 184 | st.number_input('a', key = 'a') 185 | st.number_input('b', key = 'b') 186 | st.form_submit_button('add', on_click=sum) 187 |
188 |
189 |
190 |
191 | ### Use st.rerun
192 |
193 | If your process affects content above your form, another alternative is using an extra rerun. This can be less resource-efficient though, and may be less desirable that the above options.
194 |
195 | python 196 | import streamlit as st 197 | 198 | if 'sum' not in st.session_state: 199 | st.session_state.sum = '' 200 | 201 | col1,col2 = st.columns(2) 202 | col1.title('Sum:') 203 | if isinstance(st.session_state.sum, float): 204 | col2.title(f'{st.session_state.sum:.2f}') 205 | 206 | with st.form('addition'): 207 | a = st.number_input('a') 208 | b = st.number_input('b') 209 | submit = st.form_submit_button('add') 210 | 211 | # The value of st.session_state.sum is updated at the end of the script rerun, 212 | # so the displayed value at the top in col2 does not show the new sum. Trigger 213 | # a second rerun when the form is submitted to update the value above. 214 | st.session_state.sum = a + b 215 | if submit: 216 | st.rerun() 217 |
218 |
219 |
220 |
221 | ## Limitations
222 |
223 | - Every form must contain a st.form_submit_button
.
224 | - st.button
and st.download_button
cannot be added to a form.
225 | - st.form
cannot be embedded inside another st.form
.
226 | - Callback functions can only be assigned to st.form_submit_button
within a form; no other widgets in a form can have a callback.
227 | - Interdependent widgets within a form are unlikely to be particularly useful. If you pass widget1
's value into widget2
when they are both inside a form, then widget2
will only update when the form is submitted.
228 |
1 | ---
2 | title: Working with fragments
3 | slug: /develop/concepts/architecture/fragments
4 | ---
5 |
6 | # Working with fragments
7 |
8 | Reruns are a central part of every Streamlit app. When users interact with widgets, your script reruns from top to bottom, and your app's frontend is updated. Streamlit provides several features to help you develop your app within this execution model. Streamlit version 1.37.0 introduced fragments to allow rerunning a portion of your code instead of your full script. As your app grows larger and more complex, these fragment reruns help your app be efficient and performant. Fragments give you finer, easy-to-understand control over your app's execution flow.
9 |
10 | Before you read about fragments, we recommend having a basic understanding of caching, Session State, and forms.
11 |
12 | ## Use cases for fragments
13 |
14 | Fragments are versatile and applicable to a wide variety of circumstances. Here are just a few, common scenarios where fragments are useful:
15 |
16 | - Your app has multiple visualizations and each one takes time to load, but you have a filter input that only updates one of them.
17 | - You have a dynamic form that doesn't need to update the rest of your app (until the form is complete).
18 | - You want to automatically update a single component or group of components to stream data.
19 |
20 | ## Defining and calling a fragment
21 |
22 | Streamlit provides a decorator (st.fragment
) to turn any function into a fragment function. When you call a fragment function that contains a widget function, a user triggers a fragment rerun instead of a full rerun when they interact with that fragment's widget. During a fragment rerun, only your fragment function is re-executed. Anything within the main body of your fragment is updated on the frontend, while the rest of your app remains the same. We'll describe fragments written across multiple containers later on.
23 |
24 | Here is a basic example of defining and calling a fragment function. Just like with caching, remember to call your function after defining it.
25 |
26 | python 27 | import streamlit as st 28 | 29 | @st.fragment 30 | def fragment_function(): 31 | if st.button("Hi!"): 32 | st.write("Hi back!") 33 | 34 | fragment_function() 35 |
36 |
37 | If you want the main body of your fragment to appear in the sidebar or another container, call your fragment function inside a context manager.
38 |
39 | python 40 | with st.sidebar: 41 | fragment_function() 42 |
43 |
44 | ### Fragment execution flow
45 |
46 | Consider the following code with the explanation and diagram below.
47 |
48 | python 49 | import streamlit as st 50 | 51 | st.title("My Awesome App") 52 | 53 | @st.fragment() 54 | def toggle_and_text(): 55 | cols = st.columns(2) 56 | cols[0].toggle("Toggle") 57 | cols[1].text_area("Enter text") 58 | 59 | @st.fragment() 60 | def filter_and_file(): 61 | cols = st.columns(2) 62 | cols[0].checkbox("Filter") 63 | cols[1].file_uploader("Upload image") 64 | 65 | toggle_and_text() 66 | cols = st.columns(2) 67 | cols[0].selectbox("Select", [1,2,3], None) 68 | cols[1].button("Update") 69 | filter_and_file() 70 |
71 |
72 | When a user interacts with an input widget inside a fragment, only the fragment reruns instead of the full script. When a user interacts with an input widget outside a fragment, the full script reruns as usual.
73 |
74 | If you run the code above, the full script will run top to bottom on your app's initial load. If you flip the toggle button in your running app, the first fragment (toggle_and_text()
) will rerun, redrawing the toggle and text area while leaving everything else unchanged. If you click the checkbox, the second fragment (filter_and_file()
) will rerun and consequently redraw the checkbox and file uploader. Everything else remains unchanged. Finally, if you click the update button, the full script will rerun, and Streamlit will redraw everything.
75 |
76 |
77 |
78 | ## Fragment return values and interacting with the rest of your app
79 |
80 | Streamlit ignores fragment return values during fragment reruns, so defining return values for your fragment functions is not recommended. Instead, if your fragment needs to share data with the rest of your app, use Session State. Fragments are just functions in your script, so they can access Session State, imported modules, and other Streamlit elements like containers. If your fragment writes to any container created outside of itself, note the following difference in behavior:
81 |
82 | - Elements drawn in the main body of your fragment are cleared and redrawn in place during a fragment rerun. Repeated fragment reruns will not cause additional elements to appear.
83 | - Elements drawn to containers outside the main body of fragment will not be cleared with each fragment rerun. Instead, Streamlit will draw them additively and these elements will accumulate until the next full-script rerun.
84 | - A fragment can't draw widgets in containers outside of the main body of the fragment. Widgets can only go in the main body of a fragment.
85 |
86 | To prevent elements from accumulating in outside containers, use
st.empty
containers. For a related tutorial, see Create a fragment across multiple containers.
87 |
88 | If you need to trigger a full-script rerun from inside a fragment, call st.rerun
. For a related tutorial, see Trigger a full-script rerun from inside a fragment.
89 |
90 | ## Automate fragment reruns
91 |
92 | st.fragment
includes a convenient run_every
parameter that causes the fragment to rerun automatically at the specified time interval. These reruns are in addition to any reruns (fragment or full-script) triggered by your user. The automatic fragment reruns will continue even if your user is not interacting with your app. This is a great way to show a live data stream or status on a running background job, efficiently updating your rendered data and only your rendered data.
93 |
94 | python 95 | @st.fragment(run_every="10s") 96 | def auto_function(): 97 | # This will update every 10 seconds! 98 | df = get_latest_updates() 99 | st.line_chart(df) 100 | 101 | auto_function() 102 |
103 |
104 | For a related tutorial, see Start and stop a streaming fragment.
105 |
106 | ## Compare fragments to other Streamlit features
107 |
108 | ### Fragments vs forms
109 |
110 | Here is a comparison between fragments and forms:
111 |
112 | - Forms allow users to interact with widgets without rerunning your app. Streamlit does not send user actions within a form to your app's Python backend until the form is submitted. Widgets within a form can not dynamically update other widgets (in or out of the form) in real-time.
113 | - Fragments run independently from the rest of your code. As your users interact with fragment widgets, their actions are immediately processed by your app's Python backend and your fragment code is rerun. Widgets within a fragment can dynamically update other widgets within the same fragment in real-time.
114 |
115 | A form batches user input without interaction between any widgets. A fragment immediately processes user input but limits the scope of the rerun.
116 |
117 | ### Fragments vs callbacks
118 |
119 | Here is a comparison between fragments and callbacks:
120 |
121 | - Callbacks allow you to execute a function at the beginning of a script rerun. A callback is a single prefix to your script rerun.
122 | - Fragments allow you to rerun a portion of your script. A fragment is a repeatable postfix to your script, running each time a user interacts with a fragment widget, or automatically in sequence when run_every
is set.
123 |
124 | When callbacks render elements to your page, they are rendered before the rest of your page elements. When fragments render elements to your page, they are updated with each fragment rerun (unless they are written to containers outside of the fragment, in which case they accumulate there).
125 |
126 | ### Fragments vs custom components
127 |
128 | Here is a comparison between fragments and custom components:
129 |
130 | - Components are custom frontend code that can interact with the Python code, native elements, and widgets in your Streamlit app. Custom components extend what’s possible with Streamlit. They follow the normal Streamlit execution flow.
131 | - Fragments are parts of your app that can rerun independently of the full app. Fragments can be composed of multiple Streamlit elements, widgets, or any Python code.
132 |
133 | A fragment can include one or more custom components. A custom component could not easily include a fragment!
134 |
135 | ### Fragments vs caching
136 |
137 | Here is a comparison between fragments and caching:
138 |
139 | - Caching: allows you to skip over a function and return a previously computed value. When you use caching, you execute everything except the cached function (if you've already run it before).
140 | - Fragments: allow you to freeze most of your app and just execute the fragment. When you use fragments, you execute only the fragment (when triggering a fragment rerun).
141 |
142 | Caching saves you from unnecessarily running a piece of your app while the rest runs. Fragments save you from running your full app when you only want to run one piece.
143 |
144 | ## Limitations and unsupported behavior
145 |
146 | - Fragments can't detect a change in input values. It is best to use Session State for dynamic input and output for fragment functions.
147 | - Using caching and fragments on the same function is unsupported.
148 | - Fragments can't render widgets in externally-created containers; widgets can only be in the main body of a fragment.
149 |
1 | ---
2 | title: Run your Streamlit app
3 | slug: /develop/concepts/architecture/run-your-app
4 | ---
5 |
6 | # Run your Streamlit app
7 |
8 | Working with Streamlit is simple. First you sprinkle a few Streamlit commands into a normal Python script, and then you run it. We list few ways to run your script, depending on your use case.
9 |
10 | ## Use streamlit run
11 |
12 | Once you've created your script, say your_script.py
, the easiest way to run it is with streamlit run
:
13 |
14 | bash 15 | streamlit run your_script.py 16 |
17 |
18 | As soon as you run the script as shown above, a local Streamlit server will spin up and your app will open in a new tab in your default web browser.
19 |
20 | ### Pass arguments to your script
21 |
22 | When passing your script some custom arguments, they must be passed after two dashes. Otherwise the arguments get interpreted as arguments to Streamlit itself:
23 |
24 | bash 25 | streamlit run your_script.py [-- script args] 26 |
27 |
28 | ### Pass a URL to streamlit run
29 |
30 | You can also pass a URL to streamlit run
! This is great when your script is hosted remotely, such as a GitHub Gist. For example:
31 |
32 | bash 33 | streamlit run https://raw.githubusercontent.com/streamlit/demo-uber-nyc-pickups/master/streamlit_app.py 34 |
35 |
36 | ## Run Streamlit as a Python module
37 |
38 | Another way of running Streamlit is to run it as a Python module. This is useful when configuring an IDE like PyCharm to work with Streamlit:
39 |
40 | bash 41 | # Running 42 | python -m streamlit run your_script.py 43 |
44 |
45 | bash 46 | # is equivalent to: 47 | streamlit run your_script.py 48 |
49 |
1 | ---
2 | title: Add statefulness to apps
3 | slug: /develop/concepts/architecture/session-state
4 | ---
5 |
6 | # Add statefulness to apps
7 |
8 | ## What is State?
9 |
10 | We define access to a Streamlit app in a browser tab as a session. For each browser tab that connects to the Streamlit server, a new session is created. Streamlit reruns your script from top to bottom every time you interact with your app. Each reruns takes place in a blank slate: no variables are shared between runs.
11 |
12 | Session State is a way to share variables between reruns, for each user session. In addition to the ability to store and persist state, Streamlit also exposes the ability to manipulate state using Callbacks. Session state also persists across pages inside a multipage app.
13 |
14 | In this guide, we will illustrate the usage of Session State and Callbacks as we build a stateful Counter app.
15 |
16 | For details on the Session State and Callbacks API, please refer to our Session State API Reference Guide.
17 |
18 | Also, check out this Session State basics tutorial video by Streamlit Developer Advocate Dr. Marisa Smith to get started:
19 |
20 |
21 |
22 | ## Build a Counter
23 |
24 | Let's call our script counter.py
. It initializes a count
variable and has a button to increment the value stored in the count
variable:
25 |
26 | python 27 | import streamlit as st 28 | 29 | st.title('Counter Example') 30 | count = 0 31 | 32 | increment = st.button('Increment') 33 | if increment: 34 | count += 1 35 | 36 | st.write('Count = ', count) 37 |
38 |
39 | No matter how many times we press the Increment button in the above app, the count
remains at 1. Let's understand why:
40 |
41 | - Each time we press the Increment button, Streamlit reruns counter.py
from top to bottom, and with every run, count
gets initialized to 0
.
42 | - Pressing Increment subsequently adds 1 to 0, thus count=1
no matter how many times we press Increment.
43 |
44 | As we'll see later, we can avoid this issue by storing count
as a Session State variable. By doing so, we're indicating to Streamlit that it should maintain the value stored inside a Session State variable across app reruns.
45 |
46 | Let's learn more about the API to use Session State.
47 |
48 | ### Initialization
49 |
50 | The Session State API follows a field-based API, which is very similar to Python dictionaries:
51 |
52 | python 53 | import streamlit as st 54 | 55 | # Check if 'key' already exists in session_state 56 | # If not, then initialize it 57 | if 'key' not in st.session_state: 58 | st.session_state['key'] = 'value' 59 | 60 | # Session State also supports the attribute based syntax 61 | if 'key' not in st.session_state: 62 | st.session_state.key = 'value' 63 |
64 |
65 | ### Reads and updates
66 |
67 | Read the value of an item in Session State by passing the item to st.write
:
68 |
69 | python 70 | import streamlit as st 71 | 72 | if 'key' not in st.session_state: 73 | st.session_state['key'] = 'value' 74 | 75 | # Reads 76 | st.write(st.session_state.key) 77 | 78 | # Outputs: value 79 |
80 |
81 | Update an item in Session State by assigning it a value:
82 |
83 | python 84 | import streamlit as st 85 | 86 | if 'key' not in st.session_state: 87 | st.session_state['key'] = 'value' 88 | 89 | # Updates 90 | st.session_state.key = 'value2' # Attribute API 91 | st.session_state['key'] = 'value2' # Dictionary like API 92 |
93 |
94 | Streamlit throws an exception if an uninitialized variable is accessed:
95 |
96 | python 97 | import streamlit as st 98 | 99 | st.write(st.session_state['value']) 100 | 101 | # Throws an exception! 102 |
103 |
104 |
105 |
106 | Let's now take a look at a few examples that illustrate how to add Session State to our Counter app.
107 |
108 | ### Example 1: Add Session State
109 |
110 | Now that we've got a hang of the Session State API, let's update our Counter app to use Session State:
111 |
112 |
python 113 | import streamlit as st 114 | 115 | st.title('Counter Example') 116 | if 'count' not in st.session_state: 117 | st.session_state.count = 0 118 | 119 | increment = st.button('Increment') 120 | if increment: 121 | st.session_state.count += 1 122 | 123 | st.write('Count = ', st.session_state.count) 124 |
125 |
126 | As you can see in the above example, pressing the Increment button updates the count
each time.
127 |
128 | ### Example 2: Session State and Callbacks
129 |
130 | Now that we've built a basic Counter app using Session State, let's move on to something a little more complex. The next example uses Callbacks with Session State.
131 |
132 | Callbacks: A callback is a Python function which gets called when an input widget changes. Callbacks can be used with widgets using the parameters on_change
(or on_click
), args
, and kwargs
. The full Callbacks API can be found in our Session State API Reference Guide.
133 |
134 | python 135 | import streamlit as st 136 | 137 | st.title('Counter Example using Callbacks') 138 | if 'count' not in st.session_state: 139 | st.session_state.count = 0 140 | 141 | def increment_counter(): 142 | st.session_state.count += 1 143 | 144 | st.button('Increment', on_click=increment_counter) 145 | 146 | st.write('Count = ', st.session_state.count) 147 |
148 |
149 | Now, pressing the Increment button updates the count each time by calling the increment_counter()
function.
150 |
151 | ### Example 3: Use args and kwargs in Callbacks
152 |
153 | Callbacks also support passing arguments using the args
parameter in a widget:
154 |
155 | python 156 | import streamlit as st 157 | 158 | st.title('Counter Example using Callbacks with args') 159 | if 'count' not in st.session_state: 160 | st.session_state.count = 0 161 | 162 | increment_value = st.number_input('Enter a value', value=0, step=1) 163 | 164 | def increment_counter(increment_value): 165 | st.session_state.count += increment_value 166 | 167 | increment = st.button('Increment', on_click=increment_counter, 168 | args=(increment_value, )) 169 | 170 | st.write('Count = ', st.session_state.count) 171 |
172 |
173 | Additionally, we can also use the kwargs
parameter in a widget to pass named arguments to the callback function as shown below:
174 |
175 | python 176 | import streamlit as st 177 | 178 | st.title('Counter Example using Callbacks with kwargs') 179 | if 'count' not in st.session_state: 180 | st.session_state.count = 0 181 | 182 | def increment_counter(increment_value=0): 183 | st.session_state.count += increment_value 184 | 185 | def decrement_counter(decrement_value=0): 186 | st.session_state.count -= decrement_value 187 | 188 | st.button('Increment', on_click=increment_counter, 189 | kwargs=dict(increment_value=5)) 190 | 191 | st.button('Decrement', on_click=decrement_counter, 192 | kwargs=dict(decrement_value=1)) 193 | 194 | st.write('Count = ', st.session_state.count) 195 |
196 |
197 | ### Example 4: Forms and Callbacks
198 |
199 | Say we now want to not only increment the count
, but also store when it was last updated. We illustrate doing this using Callbacks and st.form
:
200 |
201 | python 202 | import streamlit as st 203 | import datetime 204 | 205 | st.title('Counter Example') 206 | if 'count' not in st.session_state: 207 | st.session_state.count = 0 208 | st.session_state.last_updated = datetime.time(0,0) 209 | 210 | def update_counter(): 211 | st.session_state.count += st.session_state.increment_value 212 | st.session_state.last_updated = st.session_state.update_time 213 | 214 | with st.form(key='my_form'): 215 | st.time_input(label='Enter the time', value=datetime.datetime.now().time(), key='update_time') 216 | st.number_input('Enter a value', value=0, step=1, key='increment_value') 217 | submit = st.form_submit_button(label='Update', on_click=update_counter) 218 | 219 | st.write('Current Count = ', st.session_state.count) 220 | st.write('Last Updated = ', st.session_state.last_updated) 221 |
222 |
223 | ## Advanced concepts
224 |
225 | ### Session State and Widget State association
226 |
227 | Session State provides the functionality to store variables across reruns. Widget state (i.e. the value of a widget) is also stored in a session.
228 |
229 | For simplicity, we have unified this information in one place. i.e. the Session State. This convenience feature makes it super easy to read or write to the widget's state anywhere in the app's code. Session State variables mirror the widget value using the key
argument.
230 |
231 | We illustrate this with the following example. Let's say we have an app with a slider to represent temperature in Celsius. We can set and get the value of the temperature widget by using the Session State API, as follows:
232 |
233 | python 234 | import streamlit as st 235 | 236 | if "celsius" not in st.session_state: 237 | # set the initial default value of the slider widget 238 | st.session_state.celsius = 50.0 239 | 240 | st.slider( 241 | "Temperature in Celsius", 242 | min_value=-100.0, 243 | max_value=100.0, 244 | key="celsius" 245 | ) 246 | 247 | # This will get the value of the slider widget 248 | st.write(st.session_state.celsius) 249 |
250 |
251 | There is a limitation to setting widget values using the Session State API.
252 |
253 |
254 |
255 | Streamlit does not allow setting widget values via the Session State API for st.button
and st.file_uploader
.
256 |
257 |
258 |
259 | The following example will raise a StreamlitAPIException
on trying to set the state of st.button
via the Session State API:
260 |
261 | python 262 | import streamlit as st 263 | 264 | if 'my_button' not in st.session_state: 265 | st.session_state.my_button = True 266 | # Streamlit will raise an Exception on trying to set the state of button 267 | 268 | st.button('Submit', key='my_button') 269 |
270 |
271 |
272 |
273 | ### Serializable Session State
274 |
275 | Serialization refers to the process of converting an object or data structure into a format that can be persisted and shared, and allowing you to recover the data’s original structure. Python’s built-in pickle module serializes Python objects to a byte stream ("pickling") and deserializes the stream into an object ("unpickling").
276 |
277 | By default, Streamlit’s Session State allows you to persist any Python object for the duration of the session, irrespective of the object’s pickle-serializability. This property lets you store Python primitives such as integers, floating-point numbers, complex numbers and booleans, dataframes, and even lambdas returned by functions. However, some execution environments may require serializing all data in Session State, so it may be useful to detect incompatibility during development, or when the execution environment will stop supporting it in the future.
278 |
279 | To that end, Streamlit provides a
runner.enforceSerializableSessionState
configuration option that, when set to true
, only allows pickle-serializable objects in Session State. To enable the option, either create a global or project config file with the following or use it as a command-line flag:
280 |
281 | toml 282 | # .streamlit/config.toml 283 | [runner] 284 | enforceSerializableSessionState = true 285 |
286 |
287 | By "pickle-serializable", we mean calling pickle.dumps(obj)
should not raise a PicklingError
exception. When the config option is enabled, adding unserializable data to session state should result in an exception. E.g.,
288 |
289 | python 290 | import streamlit as st 291 | 292 | def unserializable_data(): 293 | return lambda x: x 294 | 295 | #👇 results in an exception when enforceSerializableSessionState is on 296 | st.session_state.unserializable = unserializable_data() 297 |
298 |
299 |
300 |
301 |
302 |
303 | When
runner.enforceSerializableSessionState
is set to true
, Session State implicitly uses the pickle
module, which is known to be insecure. Ensure all data saved and retrieved from Session State is trusted because it is possible to construct malicious pickle data that will execute arbitrary code during unpickling. Never load data that could have come from an untrusted source in an unsafe mode or that could have been tampered with. Only load data you trust.
304 |
305 |
306 |
307 | ### Caveats and limitations
308 |
309 | Here are some limitations to keep in mind when using Session State:
310 |
311 | - Session State exists for as long as the tab is open and connected to the Streamlit server. As soon as you close the tab, everything stored in Session State is lost.
312 | - Session State is not persisted. If the Streamlit server crashes, then everything stored in Session State gets wiped
313 | - For caveats and limitations with the Session State API, please see the API limitations.
314 |
1 | ---
2 | title: Widget behavior
3 | slug: /develop/concepts/architecture/widget-behavior
4 | ---
5 |
6 | # Understanding widget behavior
7 |
8 | Widgets (like st.button
, st.selectbox
, and st.text_input
) are at the heart of Streamlit apps. They are the interactive elements of Streamlit that pass information from your users into your Python code. Widgets are magical and often work how you want, but they can have surprising behavior in some situations. Understanding the different parts of a widget and the precise order in which events occur helps you achieve your desired results.
9 |
10 | This guide covers advanced concepts about widgets. Generally, it begins with simpler concepts and increases in complexity. For most beginning users, these details won't be important to know right away. When you want to dynamically change widgets or preserve widget information between pages, these concepts will be important to understand. We recommend having a basic understanding of Session State before reading this guide.
11 |
12 |
13 |
14 | 1. The actions of one user do not affect the widgets of any other user.
15 | 2. A widget function call returns the widget's current value, which is a simple Python type. (e.g. st.button
returns a boolean value.)
16 | 3. Widgets return their default values on their first call before a user interacts with them.
17 | 4. A widget's identity depends on the arguments passed to the widget function. Changing a widget's label, min or max value, default value, placeholder text, help text, or key will cause it to reset.
18 | 5. If you don't call a widget function in a script run, Streamlit will delete the widget's information—including its key-value pair in Session State. If you call the same widget function later, Streamlit treats it as a new widget.
19 |
20 | The last two points (widget identity and widget deletion) are the most relevant when dynamically changing widgets or working with multi-page applications. This is covered in detail later in this guide: Statefulness of widgets and Widget life cycle.
21 |
22 |
23 |
24 | ## Anatomy of a widget
25 |
26 | There are four parts to keep in mind when using widgets:
27 |
28 | 1. The frontend component as seen by the user.
29 | 2. The backend value or value as seen through st.session_state
.
30 | 3. The key of the widget used to access its value via st.session_state
.
31 | 4. The return value given by the widget's function.
32 |
33 | ### Widgets are session dependent
34 |
35 | Widget states are dependent on a particular session (browser connection). The actions of one user do not affect the widgets of any other user. Furthermore, if a user opens up multiple tabs to access an app, each tab will be a unique session. Changing a widget in one tab will not affect the same widget in another tab.
36 |
37 | ### Widgets return simple Python data types
38 |
39 | The value of a widget as seen through st.session_state
and returned by the widget function are of simple Python types. For example, st.button
returns a boolean value and will have the same boolean value saved in st.session_state
if using a key. The first time a widget function is called (before a user interacts with it), it will return its default value. (e.g. st.selectbox
returns the first option by default.) Default values are configurable for all widgets with a few special exceptions like st.button
and st.file_uploader
.
40 |
41 | ### Keys help distinguish widgets and access their values
42 |
43 | Widget keys serve two purposes:
44 |
45 | 1. Distinguishing two otherwise identical widgets.
46 | 2. Creating a means to access and manipulate the widget's value through st.session_state
.
47 |
48 | Whenever possible, Streamlit updates widgets incrementally on the frontend instead of rebuilding them with each rerun. This means Streamlit assigns an ID to each widget from the arguments passed to the widget function. A widget's ID is based on parameters such as label, min or max value, default value, placeholder text, help text, and key. The page where the widget appears also factors into a widget's ID. If you have two widgets of the same type with the same arguments on the same page, you will get a DuplicateWidgetID
error. In this case, assign unique keys to the two widgets.
49 |
50 | #### Streamlit can't understand two identical widgets on the same page
51 |
52 | python 53 | # This will cause a DuplicateWidgetID error. 54 | st.button("OK") 55 | st.button("OK") 56 |
57 |
58 | #### Use keys to distinguish otherwise identical widgets
59 |
60 | python 61 | st.button("OK", key="privacy") 62 | st.button("OK", key="terms") 63 |
64 |
65 | ## Order of operations
66 |
67 | When a user interacts with a widget, the order of logic is:
68 |
69 | 1. Its value in st.session_state
is updated.
70 | 2. The callback function (if any) is executed.
71 | 3. The page reruns with the widget function returning its new value.
72 |
73 | If the callback function writes anything to the screen, that content will appear above the rest of the page. A callback function runs as a prefix to the script rerunning. Consequently, that means anything written via a callback function will disappear as soon as the user performs their next action. Other widgets should generally not be created within a callback function.
74 |
75 |
76 |
77 | If a callback function is passed any args or kwargs, those arguments will be established when the widget is rendered. In particular, if you want to use a widget's new value in its own callback function, you cannot pass that value to the callback function via the args
parameter; you will have to assign a key to the widget and look up its new value using a call to st.session_state
within the callback function.
78 |
79 |
80 |
81 | ### Using callback functions with forms
82 |
83 | Using a callback function with a form requires consideration of this order of operations.
84 |
85 | python 86 | import streamlit as st 87 | 88 | if "attendance" not in st.session_state: 89 | st.session_state.attendance = set() 90 | 91 | 92 | def take_attendance(): 93 | if st.session_state.name in st.session_state.attendance: 94 | st.info(f"{st.session_state.name} has already been counted.") 95 | else: 96 | st.session_state.attendance.add(st.session_state.name) 97 | 98 | 99 | with st.form(key="my_form"): 100 | st.text_input("Name", key="name") 101 | st.form_submit_button("I'm here!", on_click=take_attendance) 102 |
103 |
104 |
105 |
106 | ## Statefulness of widgets
107 |
108 | As long as the defining parameters of a widget remain the same and that widget is continuously rendered on the frontend, then it will be stateful and remember user input.
109 |
110 | ### Changing parameters of a widget will reset it
111 |
112 | If any of the defining parameters of a widget change, Streamlit will see it as a new widget and it will reset. The use of manually assigned keys and default values is particularly important in this case. Note that callback functions, callback args and kwargs, label visibility, and disabling a widget do not affect a widget's identity.
113 |
114 | In this example, we have a slider whose min and max values are changed. Try interacting with each slider to change its value then change the min or max setting to see what happens.
115 |
116 | python 117 | import streamlit as st 118 | 119 | cols = st.columns([2, 1, 2]) 120 | minimum = cols[0].number_input("Minimum", 1, 5) 121 | maximum = cols[2].number_input("Maximum", 6, 10, 10) 122 | 123 | st.slider("No default, no key", minimum, maximum) 124 | st.slider("No default, with key", minimum, maximum, key="a") 125 | st.slider("With default, no key", minimum, maximum, value=5) 126 | st.slider("With default, with key", minimum, maximum, value=5, key="b") 127 |
128 |
129 |
130 |
131 | #### Updating a slider with no default value
132 |
133 | For the first two sliders above, as soon as the min or max value is changed, the sliders reset to the min value. The changing of the min or max value makes them "new" widgets from Streamlit's perspective and so they are recreated from scratch when the app reruns with the changed parameters. Since no default value is defined, each widget will reset to its min value. This is the same with or without a key since it's seen as a new widget either way. There is a subtle point to understand about pre-existing keys connecting to widgets. This will be explained further down in Widget life cycle.
134 |
135 | #### Updating a slider with a default value
136 |
137 | For the last two sliders above, a change to the min or max value will result in the widgets being seen as "new" and thus recreated like before. Since a default value of 5 is defined, each widget will reset to 5 whenever the min or max is changed. This is again the same (with or without a key).
138 |
139 | A solution to Retain statefulness when changing a widget's parameters is provided further on.
140 |
141 | ### Widgets do not persist when not continually rendered
142 |
143 | If a widget's function is not called during a script run, then none of its parts will be retained, including its value in st.session_state
. If a widget has a key and you navigate away from that widget, its key and associated value in st.session_state
will be deleted. Even temporarily hiding a widget will cause it to reset when it reappears; Streamlit will treat it like a new widget. You can either interrupt the Widget clean-up process (described at the end of this page) or save the value to another key.
144 |
145 | #### Save widget values in Session State to preserve them between pages
146 |
147 | If you want to navigate away from a widget and return to it while keeping its value, use a separate key in st.session_state
to save the information independently from the widget. In this example, a temporary key is used with a widget. The temporary key uses an underscore prefix. Hence, "_my_key"
is used as the widget key, but the data is copied to "my_key"
to preserve it between pages.
148 |
149 | python 150 | import streamlit as st 151 | 152 | def store_value(): 153 | # Copy the value to the permanent key 154 | st.session_state["my_key"] = st.session_state["_my_key"] 155 | 156 | # Copy the saved value to the temporary key 157 | st.session_state["_my_key"] = st.session_state["my_key"] 158 | st.number_input("Number of filters", key="_my_key", on_change=store_value) 159 |
160 |
161 | If this is functionalized to work with multiple widgets, it could look something like this:
162 |
163 | python 164 | import streamlit as st 165 | 166 | def store_value(key): 167 | st.session_state[key] = st.session_state["_"+key] 168 | def load_value(key): 169 | st.session_state["_"+key] = st.session_state[key] 170 | 171 | load_value("my_key") 172 | st.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"]) 173 |
174 |
175 | ## Widget life cycle
176 |
177 | When a widget function is called, Streamlit will check if it already has a widget with the same parameters. Streamlit will reconnect if it thinks the widget already exists. Otherwise, it will make a new one.
178 |
179 | As mentioned earlier, Streamlit determines a widget's ID based on parameters such as label, min or max value, default value, placeholder text, help text, and key. The page name also factors into a widget's ID. On the other hand, callback functions, callback args and kwargs, label visibility, and disabling a widget do not affect a widget's identity.
180 |
181 | ### Calling a widget function when the widget doesn't already exist
182 |
183 | If your script rerun calls a widget function with changed parameters or calls a widget function that wasn't used on the last script run:
184 |
185 | 1. Streamlit will build the frontend and backend parts of the widget, using its default value.
186 | 2. If the widget has been assigned a key, Streamlit will check if that key already exists in Session State.
187 | a. If it exists and is not currently associated with another widget, Streamlit will assign that key's value to the widget.
188 | b. Otherwise, it will assign the default value to the key in st.session_state
(creating a new key-value pair or overwriting an existing one).
189 | 3. If there are args or kwargs for a callback function, they are computed and saved at this point in time.
190 | 4. The widget value is then returned by the function.
191 |
192 | Step 2 can be tricky. If you have a widget:
193 |
194 | python 195 | st.number_input("Alpha",key="A") 196 |
197 |
198 | and you change it on a page rerun to:
199 |
200 | python 201 | st.number_input("Beta",key="A") 202 |
203 |
204 | Streamlit will see that as a new widget because of the label change. The key "A"
will be considered part of the widget labeled "Alpha"
and will not be attached as-is to the new widget labeled "Beta"
. Streamlit will destroy st.session_state.A
and recreate it with the default value.
205 |
206 | If a widget attaches to a pre-existing key when created and is also manually assigned a default value, you will get a warning if there is a disparity. If you want to control a widget's value through st.session_state
, initialize the widget's value through st.session_state
and avoid the default value argument to prevent conflict.
207 |
208 | ### Calling a widget function when the widget already exists
209 |
210 | When rerunning a script without changing a widget's parameters:
211 |
212 | 1. Streamlit will connect to the existing frontend and backend parts.
213 | 2. If the widget has a key that was deleted from st.session_state
, then Streamlit will recreate the key using the current frontend value. (e.g Deleting a key will not revert the widget to a default value.)
214 | 3. It will return the current value of the widget.
215 |
216 | ### Widget clean-up process
217 |
218 | When Streamlit gets to the end of a script run, it will delete the data for any widgets it has in memory that were not rendered on the screen. Most importantly, that means Streamlit will delete all key-value pairs in st.session_state
associated with a widget not currently on screen.
219 |
220 | ## Additional examples
221 |
222 | As promised, let's address how to retain the statefulness of widgets when changing pages or modifying their parameters. There are two ways to do this.
223 |
224 | 1. Use dummy keys to duplicate widget values in st.session_state
and protect the data from being deleted along with the widget.
225 | 2. Interrupt the widget clean-up process.
226 |
227 | The first method was shown above in Save widget values in Session State to preserve them between pages
228 |
229 | ### Interrupting the widget clean-up process
230 |
231 | To retain information for a widget with key="my_key"
, just add this to the top of every page:
232 |
233 | python 234 | st.session_state.my_key = st.session_state.my_key 235 |
236 |
237 | When you manually save data to a key in st.session_state
, it will become detached from any widget as far as the clean-up process is concerned. If you navigate away from a widget with some key "my_key"
and save data to st.session_state.my_key
on the new page, you will interrupt the widget clean-up process and prevent the key-value pair from being deleted or overwritten if another widget with the same key exists.
238 |
239 | ### Retain statefulness when changing a widget's parameters
240 |
241 | Here is a solution to our earlier example of changing a slider's min and max values. This solution interrupts the clean-up process as described above.
242 |
243 | python 244 | import streamlit as st 245 | 246 | # Set default value 247 | if "a" not in st.session_state: 248 | st.session_state.a = 5 249 | 250 | cols = st.columns(2) 251 | minimum = cols[0].number_input("Min", 1, 5, key="min") 252 | maximum = cols[1].number_input("Max", 6, 10, 10, key="max") 253 | 254 | 255 | def update_value(): 256 | # Helper function to ensure consistency between widget parameters and value 257 | st.session_state.a = min(st.session_state.a, maximum) 258 | st.session_state.a = max(st.session_state.a, minimum) 259 | 260 | 261 | # Validate the slider value before rendering 262 | update_value() 263 | st.slider("A", minimum, maximum, key="a") 264 |
265 |
266 |
267 |
268 | The update_value()
helper function is actually doing two things. On the surface, it's making sure there are no inconsistent changes to the parameters values as described. Importantly, it's also interrupting the widget clean-up process. When the min or max value of the widget changes, Streamlit sees it as a new widget on rerun. Without saving a value to st.session_state.a
, the value would be thrown out and replaced by the "new" widget's default value.
269 |
1 | --- 2 | title: Configure and customize your app 3 | slug: /develop/concepts/configuration 4 | --- 5 | 6 | # Configure and customize your app 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | Understand the types of options available to you through Streamlit configuration. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Understand how to configure SSL and TLS for your Streamlit app. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Understand how to host files alongside your app to make them accessible by URL. Use this if you want to point to files with raw HTML. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Understand how to use the theming configuration options to customize the color and appearance of your app. 39 | 40 | 41 | 42 | 43 |1 | ---
2 | title: HTTPS support
3 | slug: /develop/concepts/configuration/https-support
4 | ---
5 |
6 | # HTTPS support
7 |
8 | Many apps need to be accessed with SSL / TLS protocol or https://
.
9 |
10 | We recommend performing SSL termination in a reverse proxy or load balancer for self-hosted and production use cases, not directly in the app. Streamlit Community Cloud uses this approach, and every major cloud and app hosting platform should allow you to configure it and provide extensive documentation. You can find some of these platforms in our Deployment tutorials.
11 |
12 | To terminate SSL in your Streamlit app, you must configure server.sslCertFile
and server.sslKeyFile
. Learn how to set config options in Configuration.
13 |
14 | ## Details on usage
15 |
16 | - The configuration value should be a local file path to a cert file and key file. These must be available at the time the app starts.
17 | - Both server.sslCertFile
and server.sslKeyFile
must be specified. If only one is specified, your app will exit with an error.
18 | - This feature will not work in Community Cloud. Community Cloud already serves your app with TLS.
19 |
20 |
21 |
22 | In a production environment, we recommend performing SSL termination by the load balancer or the reverse proxy, not using this option. The use of this option in Streamlit has not gone through extensive security audits or performance tests.
23 |
24 |
25 |
26 | ## Example usage
27 |
28 | toml 29 | # .streamlit/config.toml 30 | 31 | [server] 32 | sslCertFile = '/path/to/certchain.pem' 33 | sslKeyFile = '/path/to/private.key' 34 |
35 |
1 | ---
2 | title: Working with configuration options
3 | slug: /develop/concepts/configuration/options
4 | ---
5 |
6 | # Working with configuration options
7 |
8 | Streamlit provides four different ways to set configuration options. This list is in reverse order of precedence, i.e. command line flags take precedence over environment variables when the same configuration option is provided multiple times.
9 |
10 |
11 |
12 | If you change theme settings in .streamlit/config.toml
while the app is running, these changes will reflect immediately. If you change non-theme settings in .streamlit/config.toml
while the app is running, the server needs to be restarted for changes to be reflected in the app.
13 |
14 |
15 |
16 | 1. In a global config file at ~/.streamlit/config.toml
for macOS/Linux or %userprofile%/.streamlit/config.toml
for Windows:
17 |
18 | toml 19 | [server] 20 | port = 80 21 |
22 |
23 | 2. In a per-project config file at $CWD/.streamlit/config.toml
, where
24 | $CWD
is the folder you're running Streamlit from.
25 |
26 | 3. Through STREAMLIT_*
environment variables, such as:
27 |
28 | bash 29 | export STREAMLIT_SERVER_PORT=80 30 | export STREAMLIT_SERVER_COOKIE_SECRET=dontforgottochangeme 31 |
32 |
33 | 4. As flags on the command line when running streamlit run
:
34 |
35 | bash 36 | streamlit run your_script.py --server.port 80 37 |
38 |
39 | ## Available options
40 |
41 | All available configuration options are documented in config.toml
. These options may be declared in a TOML file, as environment variables, or as command line options.
42 |
43 | When using environment variables to override config.toml
, convert the variable (including its section header) to upper snake case and add a STREAMLIT_
prefix. For example, STREAMLIT_CLIENT_SHOW_ERROR_DETAILS
is equivalent to the following in TOML:
44 |
45 | toml 46 | [client] 47 | showErrorDetails = true 48 |
49 |
50 | When using command line options to override config.toml
and environment variables, use the same case as you would in the TOML file and include the section header as a period-separated prefix. For example, the command line option --server.enableStaticServing true
is equivalent to the following:
51 |
52 | toml 53 | [server] 54 | enableStaticServing = true 55 |
56 |
57 | ## Telemetry
58 |
59 | As mentioned during the installation process, Streamlit collects usage statistics. You can find out
60 | more by reading our Privacy Notice, but the high-level
61 | summary is that although we collect telemetry data we cannot see and do not store information
62 | contained in Streamlit apps.
63 |
64 | If you'd like to opt out of usage statistics, add the following to your config file:
65 |
66 | toml 67 | [browser] 68 | gatherUsageStats = false 69 |
70 |
71 | ## Theming
72 |
73 | You can change the base colors of your app using the [theme]
section of the configuration system.
74 | To learn more, see Theming.
75 |
76 | ## View all configuration options
77 |
78 | As described in Command-line options, you can
79 | view all available configuration options using:
80 |
81 | bash 82 | streamlit config show 83 |
84 |
1 | ---
2 | title: Static file serving
3 | slug: /develop/concepts/configuration/serving-static-files
4 | ---
5 |
6 | # Static file serving
7 |
8 | Streamlit apps can host and serve small, static media files to support media embedding use cases that
9 | won't work with the normal media elements.
10 |
11 | To enable this feature, set enableStaticServing = true
under [server]
in your config file,
12 | or environment variable STREAMLIT_SERVER_ENABLE_STATIC_SERVING=true
.
13 |
14 | Media stored in the folder ./static/
relative to the running app file is served at path
15 | app/static/[filename]
, such as http://localhost:8501/app/static/cat.png
.
16 |
17 | ## Details on usage
18 |
19 | - Files with the following extensions will be served normally:
20 | - Common image types: .jpg
, .jpeg
, .png
, .gif
21 | - Common font types: .otf
, .ttf
, .woff
, .woff2
22 | - Other types: .pdf
, .xml
, .json
23 | Any other file will be sent with header Content-Type:text/plain
which will cause browsers to render in plain text.
24 | This is included for security - other file types that need to render should be hosted outside the app.
25 | - Streamlit also sets X-Content-Type-Options:nosniff
for all files rendered from the static directory.
26 | - For apps running on Streamlit Community Cloud:
27 | - Files available in the Github repo will always be served. Any files generated while the app is running,
28 | such as based on user interaction (file upload, etc), are not guaranteed to persist across user sessions.
29 | - Apps which store and serve many files, or large files, may run into resource limits and be shut down.
30 |
31 | ## Example usage
32 |
33 | - Put an image cat.png
in the folder ./static/
34 | - Add enableStaticServing = true
under [server]
in your .streamlit/config.toml
35 | - Any media in the ./static/
folder is served at the relative URL like app/static/cat.png
36 |
37 | toml 38 | # .streamlit/config.toml 39 | 40 | [server] 41 | enableStaticServing = true 42 |
43 |
44 | python 45 | # app.py 46 | import streamlit as st 47 | 48 | with st.echo(): 49 | st.title("CAT") 50 | 51 | st.markdown("[](https://streamlit.io)") 52 | 53 |
54 |
55 | Additional resources:
56 |
57 | - https://docs.streamlit.io/develop/concepts/configuration
58 | - https://static-file-serving.streamlit.app/
59 |
60 |
61 |
1 | ---
2 | title: Theming
3 | slug: /develop/concepts/configuration/theming
4 | ---
5 |
6 | # Theming
7 |
8 | In this guide, we provide examples of how Streamlit page elements are affected
9 | by the various theme config options. For a more high-level overview of
10 | Streamlit themes, see the Themes section of the
11 | main concepts documentation.
12 |
13 | Streamlit themes are defined using regular config options: a theme can be set
14 | via command line flag when starting your app using streamlit run
or by
15 | defining it in the [theme]
section of a .streamlit/config.toml
file. For
16 | more information on setting config options, please refer to the
17 | Streamlit configuration documentation.
18 |
19 | The following config options show the default Streamlit Light theme recreated
20 | in the [theme]
section of a .streamlit/config.toml
file.
21 |
22 | toml 23 | [theme] 24 | primaryColor="#FF4B4B" 25 | backgroundColor="#FFFFFF" 26 | secondaryBackgroundColor="#F0F2F6" 27 | textColor="#31333F" 28 | font="sans serif" 29 |
30 |
31 | Let's go through each of these options, providing screenshots to demonstrate
32 | what parts of a Streamlit app they affect where needed.
33 |
34 | ## primaryColor
35 |
36 | primaryColor
defines the accent color most often used throughout a Streamlit
37 | app. A few examples of Streamlit widgets that use primaryColor
include
38 | st.checkbox
, st.slider
, and st.text_input
(when focused).
39 |
40 |
41 |
42 |
43 |
44 | Any CSS color can be used as the value for primaryColor and the other color
45 | options below. This means that theme colors can be specified in hex or with
46 | browser-supported color names like "green", "yellow", and
47 | "chartreuse". They can even be defined in the RGB and HSL formats!
48 |
49 |
50 |
51 | ## backgroundColor
52 |
53 | Defines the background color used in the main content area of your app.
54 |
55 | ## secondaryBackgroundColor
56 |
57 | This color is used where a second background color is needed for added
58 | contrast. Most notably, it is the sidebar's background color. It is also used
59 | as the background color for most interactive widgets.
60 |
61 |
62 |
63 | ## textColor
64 |
65 | This option controls the text color for most of your Streamlit app.
66 |
67 | ## font
68 |
69 | Selects the font used in your Streamlit app. Valid values are
"sans serif"
,
70 | "serif"
, and "monospace"
. This option defaults to "sans serif"
if unset
71 | or invalid.
72 |
73 | Note that code blocks are always rendered using the monospace font regardless of
74 | the font selected here.
75 |
76 | ## base
77 |
78 | An easy way to define custom themes that make small changes to one of the
79 | preset Streamlit themes is to use the base
option. Using base
, the
80 | Streamlit Light theme can be recreated as a custom theme by writing the
81 | following:
82 |
83 | toml 84 | [theme] 85 | base="light" 86 |
87 |
88 | The base
option allows you to specify a preset Streamlit theme that your
89 | custom theme inherits from. Any theme config options not defined in your theme
90 | settings have their values set to those of the base theme. Valid values for
91 | base
are "light"
and "dark"
.
92 |
93 | For example, the following theme config defines a custom theme nearly identical
94 | to the Streamlit Dark theme, but with a new primaryColor
.
95 |
96 | toml 97 | [theme] 98 | base="dark" 99 | primaryColor="purple" 100 |
101 |
102 | If base
itself is omitted, it defaults to "light"
, so you can define a
103 | custom theme that changes the font of the Streamlit Light theme to serif with
104 | the following config
105 |
106 | toml 107 | [theme] 108 | font="serif" 109 |
110 |
1 | --- 2 | title: Working with connections, secrets, and user authentication 3 | slug: /develop/concepts/connections 4 | --- 5 | 6 | # Working with connections, secrets, and user authentication 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | Connect your app to remote data or a third-party API. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Set up your development environement and design your app to handle secrets securely. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Use an OpenID Connect provider to authenticate users and personalize your app. 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Check out a few reminders to follow best practices and avoid security mistakes. 39 | 40 | 41 | 42 | 43 |1 | ---
2 | title: User authentication and information
3 | slug: /develop/concepts/connections/authentication
4 | ---
5 |
6 | # User authentication and information
7 |
8 | Personalizing your app for your users is a great way to make your app more engaging.
9 |
10 | User authentication and personalization unlocks a plethora of use cases for developers, including controls for admins, a personalized stock ticker, or a chatbot app with a saved history between sessions.
11 |
12 | Before reading this guide, you should have a basic understanding of secrets management.
13 |
14 | ## OpenID Connect
15 |
16 | Streamlit supports user authentication with OpenID Connect (OIDC), which is an authentication protocol built on top of OAuth 2.0. OIDC supports authentication, but not authorization: that is, OIDC connections tell you who a user is (authentication), but don't give you the authority to impersonate them (authorization). If you need to connect with a generic OAuth 2.0 provider or have your app to act on behalf of a user, consider using or creating a custom component.
17 |
18 | Some popular OIDC providers are:
19 |
20 | - Google Identity
21 | - Microsoft Entra ID
22 | - Okta
23 | - Auth0
24 |
25 | ## st.login()
, st.user
, and st.logout()
26 |
27 | There are three commands involved with user authentication:
28 |
29 | - st.login()
redirects the user to your identity provider. After they log in, Streamlit stores an identity cookie and then redirects them to the homepage of your app in a new session.
30 | - st.user
is a dict-like object for accessing user information. It has a persistent attribute, .is_logged_in
, which you can check for the user's login status. When they are logged in, other attributes are available per your identity provider's configuration.
31 | - st.logout()
removes the identity cookie from the user's browser and redirects them to the homepage of your app in a new session.
32 |
33 | ## User cookies and logging out
34 |
35 | Streamlit checks for the identity cookie at the beginning of each new session. If a user logs in to your app in one tab and then opens a new tab, they will automatically be logged in to your app in the new tab. When you call st.logout()
in a user session, Streamlit removes the identity cookie and starts a new session. This logs the user out from the current session. However, if they were logged in to other sessions already, they will remain logged in within those sessions. The information in st.user
is updated at the beginning of a session (which is why st.login()
and st.logout()
both start new sessions after saving or deleting the identity cookie).
36 |
37 | If a user closes your app without logging out, the identity cookie will expire after 30 days. This expiration time is not configurable and is not tied to any expiration time that may be returned in your user's identity token. If you need to prevent persistent authentication in your app, check the expiration information returned by the identity provider in st.user
and manually call st.logout()
when needed.
38 |
39 | Streamlit does not modify or delete any cookies saved directly by your identity provider. For example, if you use Google as your identity provider and a user logs in to your app with Google, they will remain logged in to their Google account after they log out of your app with st.logout()
.
40 |
41 | ## Setting up an identity provider
42 |
43 | In order to use an identity provider, you must first configure your identity provider through an admin account. This typically involves setting up a client or application within the identity provider's system. Follow the documentation for your identity provider. As a general overview, an identity-provider client typically does the following:
44 |
45 | - Manages the list of your users.
46 | - Optional: Allows users to add themselves to your user list.
47 | - Declares the set of attributes passed from each user account to the client (which is then passed to your Streamlit app).
48 | - Only allows authentication requests to come from your Streamlit app.
49 | - Redirects users back to your Streamlit app after they authenticate.
50 |
51 | To configure your app, you'll need the following:
52 |
53 | - Your app's URL
54 | For example, use http://localhost:8501
for most local development cases.
55 | - A redirect URL, which is your app's URL with the pathname oauth2callback
56 | For example, http://localhost:8501/oauth2callback
for most local development cases.
57 | - A cookie secret, which should be a strong, randomly generated string
58 |
59 | After you use this information to configure your identity-provider client, you'll receive the following information from your identity provider:
60 |
61 | - Client ID
62 | - Client secret
63 | - Server metadata URL
64 |
65 | Examples for popular OIDC provider configurations are listed in the API reference for st.login()
.
66 |
67 | ## Configure your OIDC connection in Streamlit
68 |
69 | After you've configured your identity-provider client, you'll need to configure your Streamlit app, too. st.login()
uses your app's secrets.toml
file to configure your connection, similar to how st.connection()
works.
70 |
71 | Whether you have one OIDC provider or many, you'll need to have an [auth]
dictionary in secrets.toml
. You must declare redirect_uri
and cookie_secret
in the [auth]
dictionary. These two values are shared between all OIDC providers in your app.
72 |
73 | If you are only using one OIDC provider, you can put the remaining three properties (client_id
, client_secret
, and server_metadata_url
) in [auth]
. However, if you are using multiple providers, they should each have a unique name so you can declare their unique values in their own dictionaries. For example, if you name your connections "connection_1"
and "connection_2"
, put their remaining properties in dictionaries named [auth.connection_1]
and [auth.connection_2]
, respectively.
74 |
75 | ## A simple example
76 |
77 | If you use Google Identity as your identity provider, a basic configuration for local development will look like the following TOML file:
78 |
79 | .streamlit/secrets.toml
:
80 |
81 | toml 82 | [auth] 83 | redirect_uri = "http://localhost:8501/oauth2callback" 84 | cookie_secret = "xxx" 85 | client_id = "xxx" 86 | client_secret = "xxx" 87 | server_metadata_url = ( 88 | "https://accounts.google.com/.well-known/openid-configuration" 89 | ) 90 |
91 |
92 | Make sure the port in redirect_uri
matches the port you are using. The cookie_secret
should be a strong, randomly generated secret. Both the redirect_uri
and cookie_secret
should have been entered into your client configuration on Google Cloud. You must copy the client_id
and client_secret
from Google Cloud after you create your client. For some identity providers, server_metadata_url
may be unique to your client. However, for Google Cloud, a single URL is shared for OIDC clients.
93 |
94 | In your app, create a simple login flow:
95 |
96 | python 97 | import streamlit as st 98 | 99 | if not st.user.is_logged_in: 100 | if st.button("Log in with Google"): 101 | st.login() 102 | st.stop() 103 | 104 | if st.button("Log out"): 105 | st.logout() 106 | st.markdown(f"Welcome! {st.user.name}") 107 |
108 |
109 | When you use st.stop()
, your script run ends as soon as the login button is displayed. This lets you avoid nesting your entire page within a conditional block. Additionally, you can use callbacks to simplify the code further:
110 |
111 | python 112 | import streamlit as st 113 | 114 | if not st.user.is_logged_in: 115 | st.button("Log in with Google", on_click=st.login) 116 | st.stop() 117 | 118 | st.button("Log out", on_click=st.logout) 119 | st.markdown(f"Welcome! {st.user.name}") 120 |
121 |
122 | ## Using multiple OIDC providers
123 |
124 | If you use more than one OIDC provider, you'll need to declare a unique name for each. If you want to use Google Identity and Microsoft Entra ID as two providers for the same app, your configuration for local development will look like the following TOML file:
125 |
126 | .streamlit/secrets.toml
:
127 |
128 | toml 129 | [auth] 130 | redirect_uri = "http://localhost:8501/oauth2callback" 131 | cookie_secret = "xxx" 132 | 133 | [auth.google] 134 | client_id = "xxx" 135 | client_secret = "xxx" 136 | server_metadata_url = ( 137 | "https://accounts.google.com/.well-known/openid-configuration" 138 | ) 139 | 140 | [auth.microsoft] 141 | client_id = "xxx" 142 | client_secret = "xxx" 143 | server_metadata_url = ( 144 | "https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration" 145 | ) 146 |
147 |
148 | Microsoft's server metadata URL varies slightly depending on how your client is scoped. Replace {tenant}
with the appropriate value described in Microsoft's documentation for OpenID configuration.
149 |
150 | Your app code:
151 |
152 | python 153 | import streamlit as st 154 | 155 | if not st.user.is_logged_in: 156 | if st.button("Log in with Google"): 157 | st.login("google") 158 | if st.button("Log in with Microsoft"): 159 | st.login("microsoft") 160 | st.stop() 161 | 162 | if st.button("Log out"): 163 | st.logout() 164 | st.markdown(f"Welcome! {st.user.name}") 165 |
166 |
167 | Using callbacks, this would look like:
168 |
169 | python 170 | import streamlit as st 171 | 172 | if not st.user.is_logged_in: 173 | st.button("Log in with Google", on_click=st.login, args=["google"]) 174 | st.button("Log in with Microsoft", on_click=st.login, args=["microsoft"]) 175 | st.stop() 176 | 177 | st.button("Log out", on_click=st.logout) 178 | st.markdown(f"Welcome! {st.user.name}") 179 |
180 |
181 | ## Passing keywords to your identity provider
182 |
183 | To customize the behavior of your identity provider, you may need to declare additional keywords. For a complete list of OIDC parameters, see OpenID Connect Core and your provider's documentation. By default, Streamlit sets scope="openid profile email"
and prompt="select_account"
. You can change these and other OIDC parameters by passing a dictionary of settings to client_kwargs
. state
and nonce
, which are used for security, are handled automatically and don't need to be specified.
184 |
185 | For example,if you are using Auth0 and need to force users to log in every time, use prompt="login"
as described in Auth0's Customize Signup and Login Prompts. Your configuration will look like this:
186 |
187 | toml 188 | [auth] 189 | redirect_uri = "http://localhost:8501/oauth2callback" 190 | cookie_secret = "xxx" 191 | 192 | [auth.auth0] 193 | client_id = "xxx" 194 | client_secret = "xxx" 195 | server_metadata_url = ( 196 | "https://{account}.{region}.auth0.com/.well-known/openid-configuration" 197 | ) 198 | client_kwargs = { "prompt" = "login" } 199 |
200 |
1 | ---
2 | title: Connecting to data
3 | slug: /develop/concepts/connections/connecting-to-data
4 | ---
5 |
6 | # Connecting to data
7 |
8 | Most Streamlit apps need some kind of data or API access to be useful - either retrieving data to view or saving the results of some user action. This data or API is often part of some remote service, database, or other data source.
9 |
10 | Anything you can do with Python, including data connections, will generally work in Streamlit. Streamlit's tutorials are a great starting place for many data sources. However:
11 |
12 | - Connecting to data in a Python application is often tedious and annoying.
13 | - There are specific considerations for connecting to data from streamlit apps, such as caching and secrets management.
14 |
15 | Streamlit provides st.connection()
to more easily connect your Streamlit apps to data and APIs with just a few lines of code. This page provides a basic example of using the feature and then focuses on advanced usage.
16 |
17 | For a comprehensive overview of this feature, check out this video tutorial by Joshua Carroll, Streamlit's Product Manager for Developer Experience. You'll learn about the feature's utility in creating and managing data connections within your apps by using real-world examples.
18 |
19 |
20 |
21 | ## Basic usage
22 |
23 | For basic startup and usage examples, read up on the relevant data source tutorial. Streamlit has built-in connections to SQL dialects and Snowflake. We also maintain installable connections for Cloud File Storage and Google Sheets.
24 |
25 | If you are just starting, the best way to learn is to pick a data source you can access and get a minimal example working from one of the pages above 👆. Here, we will provide an ultra-minimal usage example for using a SQLite database. From there, the rest of this page will focus on advanced usage.
26 |
27 | ### A simple starting point - using a local SQLite database
28 |
29 | A local SQLite database could be useful for your app's semi-persistent data storage.
30 |
31 |
32 |
33 | Community Cloud apps do not guarantee the persistence of local file storage, so the platform may delete data stored using this technique at any time.
34 |
35 |
36 |
37 | To see the example below running live, check out the interactive demo below:
38 |
39 |
40 |
41 | #### Step 1: Install prerequisite library - SQLAlchemy
42 |
43 | All SQLConnections in Streamlit use SQLAlchemy. For most other SQL dialects, you also need to install the driver. But the SQLite driver ships with python3, so it isn't necessary.
44 |
45 | bash 46 | pip install SQLAlchemy==1.4.0 47 |
48 |
49 | #### Step 2: Set a database URL in your Streamlit secrets.toml file
50 |
51 | Create a directory and file .streamlit/secrets.toml
in the same directory your app will run from. Add the following to the file.
52 |
53 | toml 54 | # .streamlit/secrets.toml 55 | 56 | [connections.pets_db] 57 | url = "sqlite:///pets.db" 58 |
59 |
60 | #### Step 3: Use the connection in your app
61 |
62 | The following app creates a connection to the database, uses it to create a table and insert some data, then queries the data back and displays it in a data frame.
63 |
64 | python 65 | # streamlit_app.py 66 | 67 | import streamlit as st 68 | 69 | # Create the SQL connection to pets_db as specified in your secrets file. 70 | conn = st.connection('pets_db', type='sql') 71 | 72 | # Insert some data with conn.session. 73 | with conn.session as s: 74 | s.execute('CREATE TABLE IF NOT EXISTS pet_owners (person TEXT, pet TEXT);') 75 | s.execute('DELETE FROM pet_owners;') 76 | pet_owners = {'jerry': 'fish', 'barbara': 'cat', 'alex': 'puppy'} 77 | for k in pet_owners: 78 | s.execute( 79 | 'INSERT INTO pet_owners (person, pet) VALUES (:owner, :pet);', 80 | params=dict(owner=k, pet=pet_owners[k]) 81 | ) 82 | s.commit() 83 | 84 | # Query and display the data you inserted 85 | pet_owners = conn.query('select * from pet_owners') 86 | st.dataframe(pet_owners) 87 |
88 |
89 | In this example, we didn't set a ttl=
value on the call to conn.query()
, meaning Streamlit caches the result indefinitely as long as the app server runs.
90 |
91 | Now, on to more advanced topics! 🚀
92 |
93 | ## Advanced topics
94 |
95 | ### Global secrets, managing multiple apps and multiple data stores
96 |
97 | Streamlit supports a global secrets file specified in the user's home directory, such as ~/.streamlit/secrets.toml
. If you build or manage multiple apps, we recommend using a global credential or secret file for local development across apps. With this approach, you only need to set up and manage your credentials in one place, and connecting a new app to your existing data sources is effectively a one-liner. It also reduces the risk of accidentally checking in your credentials to git since they don't need to exist in the project repository.
98 |
99 | For cases where you have multiple similar data sources that you connect to during local development (such as a local vs. staging database), you can define different connection sections in your secrets or credentials file for different environments and then decide which to use at runtime. st.connection
supports this with the name=env:<MY_NAME_VARIABLE>
syntax.
100 |
101 | E.g., say I have a local and a staging MySQL database and want to connect my app to either at different times. I could create a global secrets file like this:
102 |
103 | toml 104 | # ~/.streamlit/secrets.toml 105 | 106 | [connections.local] 107 | url = "mysql://me:****@localhost:3306/local_db" 108 | 109 | [connections.staging] 110 | url = "mysql://jdoe:******@staging.acmecorp.com:3306/staging_db" 111 |
112 |
113 | Then I can configure my app connection to take its name from a specified environment variable
114 |
115 | python 116 | # streamlit_app.py 117 | import streamlit as st 118 | 119 | conn = st.connection("env:DB_CONN", "sql") 120 | df = conn.query("select * from mytable") 121 | # ... 122 |
123 |
124 | Now I can specify whether to connect to local or staging at runtime by setting the DB_CONN
environment variable.
125 |
126 | bash 127 | # connect to local 128 | DB_CONN=local streamlit run streamlit_app.py 129 | 130 | # connect to staging 131 | DB_CONN=staging streamlit run streamlit_app.py 132 |
133 |
134 | ### Advanced SQLConnection configuration
135 |
136 | The SQLConnection configuration uses SQLAlchemy create_engine()
function. It will take a single URL argument or attempt to construct a URL from several parts (username, database, host, and so on) using SQLAlchemy.engine.URL.create()
.
137 |
138 | Several popular SQLAlchemy dialects, such as Snowflake and Google BigQuery, can be configured using additional arguments to create_engine()
besides the URL. These can be passed as **kwargs
to the st.connection call directly or specified in an additional secrets section called create_engine_kwargs
.
139 |
140 | E.g. snowflake-sqlalchemy takes an additional connect_args
argument as a dictionary for configuration that isn’t supported in the URL. These could be specified as follows:
141 |
142 | toml 143 | # .streamlit/secrets.toml 144 | 145 | [connections.snowflake] 146 | url = "snowflake://<user_login_name>@<account_identifier>/" 147 | 148 | [connections.snowflake.create_engine_kwargs.connect_args] 149 | authenticator = "externalbrowser" 150 | warehouse = "xxx" 151 | role = "xxx" 152 |
153 |
154 | python 155 | # streamlit_app.py 156 | 157 | import streamlit as st 158 | 159 | # url and connect_args from secrets.toml above are picked up and used here 160 | conn = st.connection("snowflake", "sql") 161 | # ... 162 |
163 |
164 | Alternatively, this could be specified entirely in **kwargs
.
165 |
166 | python 167 | # streamlit_app.py 168 | 169 | import streamlit as st 170 | 171 | # secrets.toml is not needed 172 | conn = st.connection( 173 | "snowflake", 174 | "sql", 175 | url = "snowflake://<user_login_name>@<account_identifier>/", 176 | connect_args = dict( 177 | authenticator = "externalbrowser", 178 | warehouse = "xxx", 179 | role = "xxx", 180 | ) 181 | ) 182 | # ... 183 |
184 |
185 | You can also provide both kwargs and secrets.toml values, and they will be merged (typically, kwargs take precedence).
186 |
187 | ### Connection considerations in frequently used or long-running apps
188 |
189 | By default, connection objects are cached without expiration using st.cache_resource
. In most cases this is desired. You can do st.connection('myconn', type=MyConnection, ttl=<N>)
if you want the connection object to expire after some time.
190 |
191 | Many connection types are expected to be long-running or completely stateless, so expiration is unnecessary. Suppose a connection becomes stale (such as a cached token expiring or a server-side connection being closed). In that case, every connection has a reset()
method, which will invalidate the cached version and cause Streamlit to recreate the connection the next time it is retrieved
192 |
193 | Convenience methods like query()
and read()
will typically cache results by default using st.cache_data
without an expiration. When an app can run many different read operations with large results, it can cause high memory usage over time and results to become stale in a long-running app, the same as with any other usage of st.cache_data
. For production use cases, we recommend setting an appropriate ttl
on these read operations, such as conn.read('path/to/file', ttl="1d")
. Refer to Caching for more information.
194 |
195 | For apps that could get significant concurrent usage, ensure that you understand any thread safety implications of your connection, particularly when using a connection built by a third party. Connections built by Streamlit should provide thread-safe operations by default.
196 |
197 | ### Build your own connection
198 |
199 | Building your own basic connection implementation using an existing driver or SDK is quite straightforward in most cases. However, you can add more complex functionality with further effort. This custom implementation can be a great way to extend support to a new data source and contribute to the Streamlit ecosystem.
200 |
201 | Maintaining a tailored internal Connection implementation across many apps can be a powerful practice for organizations with frequently used access patterns and data sources.
202 |
203 | Check out the Build your own Connection page in the st.experimental connection demo app below for a quick tutorial and working implementation. This demo builds a minimal but very functional Connection on top of DuckDB.
204 |
205 |
206 |
207 | The typical steps are:
208 |
209 | 1. Declare the Connection class, extending ExperimentalBaseConnection
with the type parameter bound to the underlying connection object:
210 |
211 | python 212 | from streamlit.connections import ExperimentalBaseConnection 213 | import duckdb 214 | 215 | class DuckDBConnection(ExperimentalBaseConnection[duckdb.DuckDBPyConnection]) 216 |
217 |
218 | 2. Implement the _connect
method that reads any kwargs, external config/credential locations, and Streamlit secrets to initialize the underlying connection:
219 |
220 | python 221 | def _connect(self, **kwargs) -> duckdb.DuckDBPyConnection: 222 | if 'database' in kwargs: 223 | db = kwargs.pop('database') 224 | else: 225 | db = self._secrets['database'] 226 | return duckdb.connect(database=db, **kwargs) 227 |
228 |
229 | 3. Add useful helper methods that make sense for your connection (wrapping them in st.cache_data
where caching is desired)
230 |
231 | ### Connection-building best practices
232 |
233 | We recommend applying the following best practices to make your Connection consistent with the Connections built into Streamlit and the wider Streamlit ecosystem. These practices are especially important for Connections that you intend to distribute publicly.
234 |
235 | 1. Extend existing drivers or SDKs, and default to semantics that makes sense for their existing users.
236 |
237 | You should rarely need to implement complex data access logic from scratch when building a Connection. Use existing popular Python drivers and clients whenever possible. Doing so makes your Connection easier to maintain, more secure, and enables users to get the latest features. E.g. SQLConnection extends SQLAlchemy, FileConnection extends fsspec, GsheetsConnection extends gspread, etc.
238 |
239 | Consider using access patterns, method/argument naming, and return values that are consistent with the underlying package and familiar to existing users of that package.
240 |
241 | 2. Intuitive, easy to use read methods.
242 |
243 | Much of the power of st.connection is providing intuitive, easy-to-use read methods that enable app developers to get started quickly. Most connections should expose at least one read method that is:
244 |
245 | - Named with a simple verb, like read()
, query()
, or get()
246 | - Wrapped by st.cache_data
by default, with at least ttl=
argument supported
247 | - If the result is in a tabular format, it returns a pandas DataFrame
248 | - Provides commonly used keyword arguments (such as paging or formatting) with sensible defaults - ideally, the common case requires only 1-2 arguments.
249 |
250 | 3. Config, secrets, and precedence in _connect
method.
251 |
252 | Every Connection should support commonly used connection parameters provided via Streamlit secrets and keyword arguments. The names should match the ones used when initializing or configuring the underlying package.
253 |
254 | Additionally, where relevant, Connections should support data source specific configuration through existing standard environment variables or config / credential files. In many cases, the underlying package provides constructors or factory functions that already handle this easily.
255 |
256 | When you can specify the same connection parameters in multiple places, we recommend using the following precedence order when possible (highest to lowest):
257 |
258 | - Keyword arguments specified in the code
259 | - Streamlit secrets
260 | - data source specific configuration (if relevant)
261 |
262 | 4. Handling thread safety and stale connections.
263 |
264 | Connections should provide thread-safe operations when practical (which should be most of the time) and clearly document any considerations around this. Most underlying drivers or SDKs should provide thread-safe objects or methods - use these when possible.
265 |
266 | If the underlying driver or SDK has a risk of stateful connection objects becoming stale or invalid, consider building a low impact health check or reset/retry pattern into the access methods. The SQLConnection built into Streamlit has a good example of this pattern using tenacity and the built-in Connection.reset() method. An alternate approach is to encourage developers to set an appropriate TTL on the st.connection()
call to ensure it periodically reinitializes the connection object.
267 |
1 | ---
2 | title: Secrets management
3 | slug: /develop/concepts/connections/secrets-management
4 | ---
5 |
6 | # Secrets management
7 |
8 | Storing unencrypted secrets in a git repository is a bad practice. For applications that require access to sensitive credentials, the recommended solution is to store those credentials outside the repository - such as using a credentials file not committed to the repository or passing them as environment variables.
9 |
10 | Streamlit provides native file-based secrets management to easily store and securely access your secrets in your Streamlit app.
11 |
12 |
13 |
14 | Existing secrets management tools, such as dotenv files, AWS credentials files, Google Cloud Secret Manager, or Hashicorp Vault, will work fine in Streamlit. We just add native secrets management for times when it's useful.
15 |
16 |
17 |
18 | ## How to use secrets management
19 |
20 | ### Develop locally and set up secrets
21 |
22 | Streamlit provides two ways to set up secrets locally using TOML format:
23 |
24 | 1. In a global secrets file at ~/.streamlit/secrets.toml
for macOS/Linux or %userprofile%/.streamlit/secrets.toml
for Windows:
25 |
26 | toml 27 | # Everything in this section will be available as an environment variable 28 | db_username = "Jane" 29 | db_password = "mypassword" 30 | 31 | # You can also add other sections if you like. 32 | # The contents of sections as shown below will not become environment variables, 33 | # but they'll be easily accessible from within Streamlit anyway as we show 34 | # later in this doc. 35 | [my_other_secrets] 36 | things_i_like = ["Streamlit", "Python"] 37 |
38 |
39 | If you use the global secrets file, you don't have to duplicate secrets across several project-level files if multiple Streamlit apps share the same secrets.
40 |
41 | 2. In a per-project secrets file at $CWD/.streamlit/secrets.toml
, where $CWD
is the folder you're running Streamlit from. If both a global secrets file and a per-project secrets file exist, secrets in the per-project file overwrite those defined in the global file.
42 |
43 |
44 |
45 | Add this file to your .gitignore
so you don't commit your secrets!
46 |
47 |
48 |
49 | ### Use secrets in your app
50 |
51 | Access your secrets by querying the st.secrets
dict, or as environment variables. For example, if you enter the secrets from the section above, the code below shows you how to access them within your Streamlit app.
52 |
53 | python 54 | import streamlit as st 55 | 56 | # Everything is accessible via the st.secrets dict: 57 | 58 | st.write("DB username:", st.secrets["db_username"]) 59 | st.write("DB password:", st.secrets["db_password"]) 60 | 61 | # And the root-level secrets are also accessible as environment variables: 62 | 63 | import os 64 | 65 | st.write( 66 | "Has environment variables been set:", 67 | os.environ["db_username"] == st.secrets["db_username"], 68 | ) 69 |
70 |
71 |
72 |
73 | You can access st.secrets
via attribute notation (e.g. st.secrets.key
), in addition to key notation (e.g. st.secrets["key"]
) — like st.session_state.
74 |
75 |
76 |
77 | You can even compactly use TOML sections to pass multiple secrets as a single attribute. Consider the following secrets:
78 |
79 | toml 80 | [db_credentials] 81 | username = "my_username" 82 | password = "my_password" 83 |
84 |
85 | Rather than passing each secret as attributes in a function, you can more compactly pass the section to achieve the same result. See the notional code below, which uses the secrets above:
86 |
87 | python 88 | # Verbose version 89 | my_db.connect(username=st.secrets.db_credentials.username, password=st.secrets.db_credentials.password) 90 | 91 | # Far more compact version! 92 | my_db.connect(**st.secrets.db_credentials) 93 |
94 |
95 | ### Error handling
96 |
97 | Here are some common errors you might encounter when using secrets management.
98 |
99 | - If a .streamlit/secrets.toml
is created while the app is running, the server needs to be restarted for changes to be reflected in the app.
100 | - If you try accessing a secret, but no secrets.toml
file exists, Streamlit will raise a FileNotFoundError
exception:
101 |
102 | - If you try accessing a secret that doesn't exist, Streamlit will raise a
KeyError
exception:
103 |
104 | python 105 | import streamlit as st 106 | 107 | st.write(st.secrets["nonexistent_key"]) 108 |
109 |
110 |
111 |
112 | ### Use secrets on Streamlit Community Cloud
113 |
114 | When you deploy your app to Streamlit Community Cloud, you can use the same secrets management workflow as you would locally. However, you'll need to also set up your secrets in the Community Cloud Secrets Management console. Learn how to do so via the Cloud-specific Secrets management documentation.
115 |
1 | ---
2 | title: Security reminders
3 | slug: /develop/concepts/connections/security-reminders
4 | ---
5 |
6 | # Security reminders
7 |
8 | ## Protect your secrets
9 |
10 | Never save usernames, passwords, or security keys directly in your code or commit them to your repository.
11 |
12 | ### Use environment variables
13 |
14 | Avoid putting sensitve information in your code by using environment variables. Be sure to check out st.secrets
. Research any platform you use to follow their security best practices. If you use Streamlit Community Cloud, Secrets management allows you save environment variables and store secrets outside of your code.
15 |
16 | ### Keep .gitignore
updated
17 |
18 | If you use any sensitive or private information during development, make sure that information is saved in separate files from your code. Ensure .gitignore
is properly configured to prevent saving private information to your repository.
19 |
20 | ## Pickle warning
21 |
22 | Streamlit's st.cache_data
and st.session_state
implicitly use the pickle
module, which is known to be insecure. It is possible to construct malicious pickle data that will execute arbitrary code during unpickling. Never load data that could have come from an untrusted source in an unsafe mode or that could have been tampered with. Only load data you trust.
23 |
24 | - When using st.cache_data
, anything your function returns is pickled and stored, then unpickled on retrieval. Ensure your cached functions return trusted values. This warning also applies to st.cache
(deprecated).
25 | - When the runner.enforceSerializableSessionState
configuration option is set to true
, ensure all data saved and retrieved from Session State is trusted.
26 |
1 | ---
2 | title: Components
3 | slug: /develop/concepts/custom-components
4 | ---
5 |
6 | # Custom Components
7 |
8 | Components are third-party Python modules that extend what's possible with Streamlit.
9 |
10 | ## How to use a Component
11 |
12 | Components are super easy to use:
13 |
14 | 1. Start by finding the Component you'd like to use. Two great resources for this are:
15 |
16 | - The Component gallery
17 | - This thread,
18 | by Fanilo A. from our forums.
19 |
20 | 2. Install the Component using your favorite Python package manager. This step and all following
21 | steps are described in your component's instructions.
22 |
23 | For example, to use the fantastic AgGrid
24 | Component, you first install it with:
25 |
26 | python 27 | pip install streamlit-aggrid 28 |
29 |
30 | 3. In your Python code, import the Component as described in its instructions. For AgGrid, this step
31 | is:
32 |
33 | python 34 | from st_aggrid import AgGrid 35 |
36 |
37 | 4. ...now you're ready to use it! For AgGrid, that's:
38 |
39 | python 40 | AgGrid(my_dataframe) 41 |
42 |
43 | ## Making your own Component
44 |
45 | If you're interested in making your own component, check out the following resources:
46 |
47 | - Create a Component
48 | - Publish a Component
49 | - Components API
50 | - Blog post for when we launched Components!
51 |
52 | Alternatively, if you prefer to learn using videos, our engineer Tim Conkling has put together some
53 | amazing tutorials:
54 |
55 | ##### Video tutorial, part 1
56 |
57 |
58 |
59 | ##### Video tutorial, part 2
60 |
61 |
62 |
1 | ---
2 | title: Intro to custom components
3 | slug: /develop/concepts/custom-components/intro
4 | ---
5 |
6 | # Intro to custom components
7 |
8 | The first step in developing a Streamlit Component is deciding whether to create a static component (i.e. rendered once, controlled by Python) or to create a bi-directional component that can communicate from Python to JavaScript and back.
9 |
10 | ## Create a static component
11 |
12 | If your goal in creating a Streamlit Component is solely to display HTML code or render a chart from a Python visualization library, Streamlit provides two methods that greatly simplify the process: components.html()
and components.iframe()
.
13 |
14 | If you are unsure whether you need bi-directional communication, start here first!
15 |
16 | ### Render an HTML string
17 |
18 | While st.text
, st.markdown
and st.write
make it easy to write text to a Streamlit app, sometimes you'd rather implement a custom piece of HTML. Similarly, while Streamlit natively supports many charting libraries, you may want to implement a specific HTML/JavaScript template for a new charting library. components.html
works by giving you the ability to embed an iframe inside of a Streamlit app that contains your desired output.
19 |
20 | Example
21 |
22 | python 23 | import streamlit as st 24 | import streamlit.components.v1 as components 25 | 26 | # bootstrap 4 collapse example 27 | components.html( 28 | """ 29 | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity_no="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> 30 | <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity_no="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> 31 | <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity_no="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> 32 | <div id="accordion"> 33 | <div class="card"> 34 | <div class="card-header" id="headingOne"> 35 | <h5 class="mb-0"> 36 | <button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> 37 | Collapsible Group Item #1 38 | </button> 39 | </h5> 40 | </div> 41 | <div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"> 42 | <div class="card-body"> 43 | Collapsible Group Item #1 content 44 | </div> 45 | </div> 46 | </div> 47 | <div class="card"> 48 | <div class="card-header" id="headingTwo"> 49 | <h5 class="mb-0"> 50 | <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"> 51 | Collapsible Group Item #2 52 | </button> 53 | </h5> 54 | </div> 55 | <div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion"> 56 | <div class="card-body"> 57 | Collapsible Group Item #2 content 58 | </div> 59 | </div> 60 | </div> 61 | </div> 62 | """, 63 | height=600, 64 | ) 65 |
66 |
67 | ### Render an iframe URL
68 |
69 | components.iframe
is similar in features to components.html
, with the difference being that components.iframe
takes a URL as its input. This is used for situations where you want to include an entire page within a Streamlit app.
70 |
71 | Example
72 |
73 | python 74 | import streamlit as st 75 | import streamlit.components.v1 as components 76 | 77 | # embed streamlit docs in a streamlit app 78 | components.iframe("https://example.com", height=500) 79 |
80 |
81 | ## Create a bi-directional component
82 |
83 | A bi-directional Streamlit Component has two parts:
84 |
85 | 1. A frontend, which is built out of HTML and any other web tech you like (JavaScript, React, Vue, etc.), and gets rendered in Streamlit apps via an iframe tag.
86 | 2. A Python API, which Streamlit apps use to instantiate and talk to that frontend
87 |
88 | To make the process of creating bi-directional Streamlit Components easier, we've created a React template and a TypeScript-only template in the Streamlit Component-template GitHub repo. We also provide some example Components in the same repo.
89 |
90 | ### Development Environment Setup
91 |
92 | To build a Streamlit Component, you need the following installed in your development environment:
93 |
94 | - Python 3.9 - Python 3.13
95 | - Streamlit
96 | - nodejs
97 | - npm or yarn
98 |
99 | Clone the component-template GitHub repo, then decide whether you want to use the React.js ("template") or plain TypeScript ("template-reactless") template.
100 |
101 | 1. Initialize and build the component template frontend from the terminal:
102 |
103 | bash 104 | # React template 105 | template/my_component/frontend 106 | npm install # Initialize the project and install npm dependencies 107 | npm run start # Start the Webpack dev server 108 | 109 | # or 110 | 111 | # TypeScript-only template 112 | template-reactless/my_component/frontend 113 | npm install # Initialize the project and install npm dependencies 114 | npm run start # Start the Webpack dev server 115 |
116 |
117 | 2. From a separate terminal, run the Streamlit app (Python) that declares and uses the component:
118 |
119 | bash 120 | # React template 121 | cd template 122 | . venv/bin/activate # or similar to activate the venv/conda environment where Streamlit is installed 123 | pip install -e . # install template as editable package 124 | streamlit run my_component/example.py # run the example 125 | 126 | # or 127 | 128 | # TypeScript-only template 129 | cd template-reactless 130 | . venv/bin/activate # or similar to activate the venv/conda environment where Streamlit is installed 131 | pip install -e . # install template as editable package 132 | streamlit run my_component/example.py # run the example 133 |
134 |
135 | After running the steps above, you should see a Streamlit app in your browser that looks like this:
136 |
137 |
138 |
139 | The example app from the template shows how bi-directional communication is implemented. The Streamlit Component displays a button (
Python → JavaScript
), and the end-user can click the button. Each time the button is clicked, the JavaScript front-end increments the counter value and passes it back to Python (JavaScript → Python
), which is then displayed by Streamlit (Python → JavaScript
).
140 |
141 | ### Frontend
142 |
143 | Because each Streamlit Component is its own webpage that gets rendered into an iframe
, you can use just about any web tech you'd like to create that web page. We provide two templates to get started with in the Streamlit Components-template GitHub repo; one of those templates uses React and the other does not.
144 |
145 |
146 |
147 | Even if you're not already familiar with React, you may still want to check out the React-based
148 | template. It handles most of the boilerplate required to send and receive data from Streamlit, and
149 | you can learn the bits of React you need as you go.
150 |
151 | If you'd rather not use React, please read this section anyway! It explains the fundamentals of
152 | Streamlit ↔ Component communication.
153 |
154 |
155 | #### React
156 |
157 | The React-based template is in template/my_component/frontend/src/MyComponent.tsx
.
158 |
159 | - MyComponent.render()
is called automatically when the component needs to be re-rendered (just like in any React app)
160 | - Arguments passed from the Python script are available via the this.props.args
dictionary:
161 |
162 | python 163 | # Send arguments in Python: 164 | result = my_component(greeting="Hello", name="Streamlit") 165 |
166 |
167 | javascript 168 | // Receive arguments in frontend: 169 | let greeting = this.props.args["greeting"]; // greeting = "Hello" 170 | let name = this.props.args["name"]; // name = "Streamlit" 171 |
172 |
173 | - Use Streamlit.setComponentValue()
to return data from the component to the Python script:
174 |
175 | javascript 176 | // Set value in frontend: 177 | Streamlit.setComponentValue(3.14); 178 |
179 |
180 | python 181 | # Access value in Python: 182 | result = my_component(greeting="Hello", name="Streamlit") 183 | st.write("result = ", result) # result = 3.14 184 |
185 |
186 | When you call Streamlit.setComponentValue(new_value)
, that new value is sent to Streamlit, which then re-executes the Python script from top to bottom. When the script is re-executed, the call to my_component(...)
will return the new value.
187 |
188 | From a code flow perspective, it appears that you're transmitting data synchronously with the frontend: Python sends the arguments to JavaScript, and JavaScript returns a value to Python, all in a single function call! But in reality this is all happening asynchronously, and it's the re-execution of the Python script that achieves the sleight of hand.
189 |
190 | - Use Streamlit.setFrameHeight()
to control the height of your component. By default, the React template calls this automatically (see StreamlitComponentBase.componentDidUpdate()
). You can override this behavior if you need more control.
191 | - There's a tiny bit of magic in the last line of the file: export default withStreamlitConnection(MyComponent)
- this does some handshaking with Streamlit, and sets up the mechanisms for bi-directional data communication.
192 |
193 | #### TypeScript-only
194 |
195 | The TypeScript-only template is in template-reactless/my_component/frontend/src/MyComponent.tsx
.
196 |
197 | This template has much more code than its React sibling, in that all the mechanics of handshaking, setting up event listeners, and updating the component's frame height are done manually. The React version of the template handles most of these details automatically.
198 |
199 | - Towards the bottom of the source file, the template calls Streamlit.setComponentReady()
to tell Streamlit it's ready to start receiving data. (You'll generally want to do this after creating and loading everything that the Component relies on.)
200 | - It subscribes to Streamlit.RENDER_EVENT
to be notified of when to redraw. (This event won't be fired until setComponentReady
is called)
201 | - Within its onRender
event handler, it accesses the arguments passed in the Python script via event.detail.args
202 | - It sends data back to the Python script in the same way that the React template does—clicking on the "Click Me!" button calls Streamlit.setComponentValue()
203 | - It informs Streamlit when its height may have changed via Streamlit.setFrameHeight()
204 |
205 | #### Working with Themes
206 |
207 |
208 |
209 | Custom component theme support requires streamlit-component-lib version 1.2.0 or higher.
210 |
211 |
212 |
213 | Along with sending an args
object to your component, Streamlit also sends
214 | a theme
object defining the active theme so that your component can adjust
215 | its styling in a compatible way. This object is sent in the same message as
216 | args
, so it can be accessed via this.props.theme
(when using the React
217 | template) or event.detail.theme
(when using the plain TypeScript template).
218 |
219 | The theme
object has the following shape:
220 |
221 | json 222 | { 223 | "base": "lightORdark", 224 | "primaryColor": "someColor1", 225 | "backgroundColor": "someColor2", 226 | "secondaryBackgroundColor": "someColor3", 227 | "textColor": "someColor4", 228 | "font": "someFont" 229 | } 230 |
231 |
232 | The base
option allows you to specify a preset Streamlit theme that your custom theme inherits from. Any theme config options not defined in your theme settings have their values set to those of the base theme. Valid values for base
are "light"
and "dark"
.
233 |
234 | Note that the theme object has fields with the same names and semantics as the
235 | options in the "theme" section of the config options printed with the command
236 | streamlit config show
.
237 |
238 | When using the React template, the following CSS variables are also set
239 | automatically.
240 |
241 | css 242 | --base 243 | --primary-color 244 | --background-color 245 | --secondary-background-color 246 | --text-color 247 | --font 248 |
249 |
250 | If you're not familiar with
251 | CSS variables,
252 | the TLDR version is that you can use them like this:
253 |
254 | css 255 | .mySelector { 256 | color: var(--text-color); 257 | } 258 |
259 |
260 | These variables match the fields defined in the theme
object above, and
261 | whether to use CSS variables or the theme object in your component is a matter
262 | of personal preference.
263 |
264 | #### Other frontend details
265 |
266 | - Because you're hosting your component from a dev server (via npm run start
), any changes you make should be automatically reflected in the Streamlit app when you save.
267 | - If you want to add more packages to your component, run npm add
to add them from within your component's frontend/
directory.
268 |
269 | bash 270 | npm add baseui 271 |
272 |
273 | - To build a static version of your component, run npm run export
. See Prepare your Component for more information
274 |
275 | ### Python API
276 |
277 | components.declare_component()
is all that's required to create your Component's Python API:
278 |
279 | python 280 | import streamlit.components.v1 as components 281 | my_component = components.declare_component( 282 | "my_component", 283 | url="http://localhost:3001" 284 | ) 285 |
286 |
287 | You can then use the returned my_component
function to send and receive data with your frontend code:
288 |
289 | python 290 | # Send data to the frontend using named arguments. 291 | return_value = my_component(name="Blackbeard", ship="Queen Anne's Revenge") 292 | 293 | # `my_component`'s return value is the data returned from the frontend. 294 | st.write("Value = ", return_value) 295 |
296 |
297 | While the above is all you need to define from the Python side to have a working Component, we recommend creating a "wrapper" function with named arguments and default values, input validation and so on. This will make it easier for end-users to understand what data values your function accepts and allows for defining helpful docstrings.
298 |
299 | Please see this example from the Components-template for an example of creating a wrapper function.
300 |
301 | ### Data serialization
302 |
303 | #### Python → Frontend
304 |
305 | You send data from Python to the frontend by passing keyword args to your Component's invoke function (that is, the function returned from declare_component
). You can send the following types of data from Python to the frontend:
306 |
307 | - Any JSON-serializable data
308 | - numpy.array
309 | - pandas.DataFrame
310 |
311 | Any JSON-serializable data gets serialized to a JSON string, and deserialized to its JavaScript equivalent. numpy.array
and pandas.DataFrame
get serialized using Apache Arrow and are deserialized as instances of ArrowTable
, which is a custom type that wraps Arrow structures and provides a convenient API on top of them.
312 |
313 | Check out the CustomDataframe and SelectableDataTable Component example code for more context on how to use ArrowTable
.
314 |
315 | #### Frontend → Python
316 |
317 | You send data from the frontend to Python via the Streamlit.setComponentValue()
API (which is part of the template code). Unlike arg-passing from Python → frontend, this API takes a single value. If you want to return multiple values, you'll need to wrap them in an Array
or Object
.
318 |
319 | Custom Components can send JSON-serializable data from the frontend to Python, as well as Apache Arrow ArrowTable
s to represent dataframes.
320 |
1 | ---
2 | title: Create a Component
3 | slug: /develop/concepts/custom-components/create
4 | ---
5 |
6 | # Create a Component
7 |
8 |
9 |
10 | If you are only interested in using Streamlit Components, then you can skip this section and
11 | head over to the Streamlit Components Gallery to find and install
12 | components created by the community!
13 |
14 |
15 |
16 | Developers can write JavaScript and HTML "components" that can be rendered in Streamlit apps. Streamlit Components can receive data from, and also send data to, Streamlit Python scripts.
17 |
18 | Streamlit Components let you expand the functionality provided in the base Streamlit package. Use Streamlit Components to create the needed functionality for your use-case, then wrap it up in a Python package and share with the broader Streamlit community!
19 |
20 | Types of Streamlit Components you could create include:
21 |
22 | - Custom versions of existing Streamlit elements and widgets, such as st.slider
or st.file_uploader
.
23 | - Completely new Streamlit elements and widgets by wrapping existing React.js, Vue.js, or other JavaScript widget toolkits.
24 | - Rendering Python objects having methods that output HTML, such as IPython __repr_html__
.
25 | - Convenience functions for commonly-used web features like GitHub gists and Pastebin.
26 |
27 | Check out these Streamlit Components tutorial videos by Streamlit engineer Tim Conkling to get started:
28 |
29 | ## Part 1: Setup and Architecture
30 |
31 |
32 |
33 | ## Part 2: Make a Slider Widget
34 |
35 |
36 |
1 | --- 2 | title: Limitations of custom components 3 | slug: /develop/concepts/custom-components/limitations 4 | --- 5 | 6 | # Limitations of custom components 7 | 8 | ## How do Streamlit Components differ from functionality provided in the base Streamlit package? 9 | 10 | - Streamlit Components are wrapped up in an iframe, which gives you the ability to do whatever you want (within the iframe) using any web technology you like. 11 | 12 | ## What types of things aren't possible with Streamlit Components? 13 | 14 | Because each Streamlit Component gets mounted into its own sandboxed iframe, this implies a few limitations on what is possible with Components: 15 | 16 | - Can't communicate with other Components: Components can’t contain (or otherwise communicate with) other components, so Components cannot be used to build something like a grid layout. 17 | - Can't modify CSS: A Component can’t modify the CSS that the rest of the Streamlit app uses, so you can't create something to put the app in dark mode, for example. 18 | - Can't add/remove elements: A Component can’t add or remove other elements of a Streamlit app, so you couldn't make something to remove the app menu, for example. 19 | 20 | ## My Component seems to be blinking/stuttering...how do I fix that? 21 | 22 | Currently, no automatic debouncing of Component updates is performed within Streamlit. The Component creator themselves can decide to rate-limit the updates they send back to Streamlit. 23 |
1 | ---
2 | title: Publish a Component
3 | slug: /develop/concepts/custom-components/publish
4 | ---
5 |
6 | # Publish a Component
7 |
8 | ## Publish to PyPI
9 |
10 | Publishing your Streamlit Component to PyPI makes it easily accessible to Python users around the world. This step is completely optional, so if you won’t be releasing your component publicly, you can skip this section!
11 |
12 |
13 |
14 | For static Streamlit Components, publishing a Python package to PyPI follows the same steps as the
15 | core PyPI packaging instructions. A static Component likely contains only Python code, so once you have your
16 | setup.py file correct and
17 | generate your distribution files, you're ready to
18 | upload to PyPI.
19 |
20 | Bi-directional Streamlit Components at minimum include both Python and JavaScript code, and as such, need a bit more preparation before they can be published on PyPI. The remainder of this page focuses on the bi-directional Component preparation process.
21 |
22 |
23 |
24 | ### Prepare your Component
25 |
26 | A bi-directional Streamlit Component varies slightly from a pure Python library in that it must contain pre-compiled frontend code. This is how base Streamlit works as well; when you pip install streamlit
, you are getting a Python library where the HTML and frontend code contained within it have been compiled into static assets.
27 |
28 | The component-template GitHub repo provides the folder structure necessary for PyPI publishing. But before you can publish, you'll need to do a bit of housekeeping:
29 |
30 | 1. Give your Component a name, if you haven't already
31 | - Rename the template/my_component/
folder to template/<component name>/
32 | - Pass your component's name as the the first argument to declare_component()
33 | 2. Edit MANIFEST.in
, change the path for recursive-include from package/frontend/build *
to <component name>/frontend/build *
34 | 3. Edit setup.py
, adding your component's name and other relevant info
35 | 4. Create a release build of your frontend code. This will add a new directory, frontend/build/
, with your compiled frontend in it:
36 |
37 | bash 38 | cd frontend 39 | npm run build 40 |
41 |
42 | 5. Pass the build folder's path as the path
parameter to declare_component
. (If you're using the template Python file, you can set _RELEASE = True
at the top of the file):
43 |
44 | python 45 | import streamlit.components.v1 as components 46 | 47 | # Change this: 48 | # component = components.declare_component("my_component", url="http://localhost:3001") 49 | 50 | # To this: 51 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 52 | build_dir = os.path.join(parent_dir, "frontend/build") 53 | component = components.declare_component("new_component_name", path=build_dir) 54 |
55 |
56 | ### Build a Python wheel
57 |
58 | Once you've changed the default my_component
references, compiled the HTML and JavaScript code and set your new component name in components.declare_component()
, you're ready to build a Python wheel:
59 |
60 | 1. Make sure you have the latest versions of setuptools, wheel, and twine
61 |
62 | 2. Create a wheel from the source code:
63 |
64 | bash 65 | # Run this from your component's top-level directory; that is, 66 | # the directory that contains `setup.py` 67 | python setup.py sdist bdist_wheel 68 |
69 |
70 | ### Upload your wheel to PyPI
71 |
72 | With your wheel created, the final step is to upload to PyPI. The instructions here highlight how to upload to Test PyPI, so that you can learn the mechanics of the process without worrying about messing anything up. Uploading to PyPI follows the same basic procedure.
73 |
74 | 1. Create an account on Test PyPI if you don't already have one
75 |
76 | - Visit https://test.pypi.org/account/register/ and complete the steps
77 |
78 | - Visit https://test.pypi.org/manage/account/#api-tokens and create a new API token. Don’t limit the token scope to a particular project, since you are creating a new project. Copy your token before closing the page, as you won’t be able to retrieve it again.
79 |
80 | 2. Upload your wheel to Test PyPI. twine
will prompt you for a username and password. For the username, use __token__. For the password, use your token value from the previous step, including the pypi-
prefix:
81 |
82 | bash 83 | python -m twine upload --repository testpypi dist/* 84 |
85 |
86 | 3. Install your newly-uploaded package in a new Python project to make sure it works:
87 |
88 | bash 89 | python -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-pkg-YOUR-USERNAME-HERE 90 |
91 |
92 | If all goes well, you're ready to upload your library to PyPI by following the instructions at https://packaging.python.org/tutorials/packaging-projects/#next-steps.
93 |
94 | Congratulations, you've created a publicly-available Streamlit Component!
95 |
96 | ## Promote your Component!
97 |
98 | We'd love to help you share your Component with the Streamlit Community! To share it:
99 |
100 | 1. If you host your code on GitHub, add the tag streamlit-component
, so that it's listed in the GitHub streamlit-component topic:
101 |
102 |
103 |
104 | 2. Post on the Streamlit Forum in Show the Community!. Use a post title similar to "New Component:
<your component name>
, a new way to do X".
105 | 3. Add your component to the Community Component Tracker.
106 | 4. Tweet us at @streamlit so that we can retweet your announcement for you.
107 |
108 | Our Components Gallery is updated approximately every month. Follow the above recommendations to maximize the liklihood of your component landing in our Components Gallery. Community Components featured in our docs are hand-curated on a less-regular basis. Popular components with many stars and good documentation are more likely to be selected.
109 |
1 | --- 2 | title: Multipage apps 3 | slug: /develop/concepts/multipage-apps 4 | description: Streamlit provides a simple way to create multipage apps. 5 | --- 6 | 7 | # Multipage apps 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | Streamlit provides multiple ways to define multipage apps. Understand the terminology and basic comparison between methods. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Learn about the preferred method for defining multipage apps.st.Page
and st.navigation
give you flexibility to organize your project directory and label your pages as you please.
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Define your multipage apps through directory structure. Place additional Python files in a pages/
directory alongside your entrypoint file and pages are automatically shown in a navigation widget inside your app's sidebar.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Understand how widget identity is tied to pages. Learn strategies to get the behavior you want out of widgets.
40 |
41 |
42 |
43 |
44 |
1 | ---
2 | title: Overview of multipage apps
3 | slug: /develop/concepts/multipage-apps/overview
4 | description: Understand Streamlit's features for creating multipage apps
5 | ---
6 |
7 | # Overview of multipage apps
8 |
9 | Streamlit provides two built-in mechanisms for creating multipage apps. The simplest method is to use a pages/
directory. However, the preferred and more customizable method is to use st.navigation
.
10 |
11 | ## st.Page
and st.navigation
12 |
13 | If you want maximum flexibility in defining your multipage app, we recommend using st.Page
and st.navigation
. With st.Page
you can declare any Python file or Callable
as a page in your app. Furthermore, you can define common elements for your pages in your entrypoint file (the file you pass to streamlit run
). With these methods, your entrypoint file becomes like a picture frame shared by all your pages.
14 |
15 | You must include st.navigation
in your entrypoint file to configure your app's navigation menu. This is also how your entrypoint file serves as the router between your pages.
16 |
17 | ## pages/
directory
18 |
19 | If you're looking for a quick and simple solution, just place a pages/
directory next to your entrypoint file. For every Python file in your pages/
directory, Streamlit will create an additional page for your app. Streamlit determines the page labels and URLs from the file name and automatically populates a navigation menu at the top of your app's sidebar.
20 |
21 | 22 | your_working_directory/ 23 | ├── pages/ 24 | │ ├── a_page.py 25 | │ └── another_page.py 26 | └── your_homepage.py 27 |
28 |
29 | Streamlit determines the page order in navigation from the filenames. You can use numerical prefixes in the filenames to adjust page order. For more information, see How pages are sorted in the sidebar. If you want to customize your navigation menu with this option, you can deactivate the default navigation through configuration (client.showSidebarNavigation = false
). Then, you can use st.page_link
to manually contruct a custom navigation menu. With st.page_link
, you can change the page label and icon in your navigation menu, but you can't change the URLs of your pages.
30 |
31 | ## Page terminology
32 |
33 | A page has four identifying pieces as follows:
34 |
35 | - Page source: This is a Python file or callable function with the page's source code.
36 | - Page label: This is how the page is identified within the navigation menu. See <i style={{ verticalAlign: "-.25em" }} class="material-icons-sharp">looks_one.
37 | - Page title: This is the content of the HTML <title>
element and how the page is identified within a browser tab. See <i style={{ verticalAlign: "-.25em" }} class="material-icons-sharp">looks_two.
38 | - Page URL pathname: This is the relative path of the page from the root URL of the app. See <i style={{ verticalAlign: "-.25em" }} class="material-icons-sharp">looks_3.
39 |
40 | Additionly, a page can have two icons as follows:
41 |
42 | - Page favicon: This is the icon next to your page title within a browser tab. See <i style={{ verticalAlign: "-.25em" }} class="material-icons-sharp">looks_4.
43 | - Page icon: This is the icon next to your page label in the navigation menu. See <i style={{ verticalAlign: "-.25em" }} class="material-icons-sharp">looks_5.
44 |
45 | Typically, the page icon and favicon are the same, but it's possible make them different.
46 |
47 | <div style={{ maxWidth: '564px', margin: 'auto' }}>
48 |
49 |
50 |
51 | ## Automatic page labels and URLs
52 |
53 | If you use
st.Page
without declaring the page title or URL pathname, Streamlit falls back on automatically determining the page label, title, and URL pathname in the same manner as when you use a pages/
directory with the default navigation menu. This section describes this naming convention which is shared between the two approaches to multipage apps.
54 |
55 | ### Parts of filenames and callables
56 |
57 | Filenames are composed of four different parts as follows (in order):
58 |
59 | 1. number
: A non-negative integer.
60 | 2. separator
: Any combination of underscore ("_"
), dash ("-"
), and space (" "
).
61 | 3. identifier
: Everything up to, but not including, ".py"
.
62 | 4. ".py"
63 |
64 | For callables, the function name is the identifier
, including any leading or trailing underscores.
65 |
66 | ### How Streamlit converts filenames into labels and titles
67 |
68 | Within the navigation menu, Streamlit displays page labels and titles as follows:
69 |
70 | 1. If your page has an identifier
, Streamlit displays the identifier
. Any underscores within the page's identifier
are treated as spaces. Therefore, leading and trailing underscores are not shown. Sequential underscores appear as a single space.
71 | 2. Otherwise, if your page has a number
but does not have an identifier
, Streamlit displays the number
, unmodified. Leading zeros are included, if present.
72 | 3. Otherwise, if your page only has a separator
with no number
and no identifier
, Streamlit will not display the page in the sidebar navigation.
73 |
74 | The following filenames and callables would all display as "Awesome page" in the sidebar navigation.
75 |
76 | - "Awesome page.py"
77 | - "Awesome_page.py"
78 | - "02Awesome_page.py"
79 | - "--Awesome_page.py"
80 | - "1_Awesome_page.py"
81 | - "33 - Awesome page.py"
82 | - Awesome_page()
83 | - _Awesome_page()
84 | - __Awesome_page__()
85 |
86 | ### How Streamlit converts filenames into URL pathnames
87 |
88 | Your app's homepage is associated to the root URL of app. For all other pages, their identifier
or number
becomes their URL pathname as follows:
89 |
90 | - If your page has an identifier
that came from a filename, Streamlit uses the identifier
with one modification. Streamlit condenses each consecutive grouping of spaces (" "
) and underscores ("_"
) to a single underscore.
91 | - Otherwise, if your page has an identifier
that came from the name of a callable, Streamlit uses the identifier
unmodified.
92 | - Otherwise, if your page has a number
but does not have an identifier
, Streamlit uses the number
. Leading zeros are included, if present.
93 |
94 | For each filename in the list above, the URL pathname would be "Awesome_page" relative to the root URL of the app. For example, if your app was running on localhost
port 8501
, the full URL would be localhost:8501/awesome_page
. For the last two callables, however, the pathname would include the leading and trailing underscores to match the callable name exactly.
95 |
96 | ## Navigating between pages
97 |
98 | The primary way users navigate between pages is through the navigation widget. Both methods for defining multipage apps include a default navigation menu that appears in the sidebar. When a user clicks this navigation widget, the app reruns and loads the selected page. Optionally, you can hide the default navigation UI and build your own with st.page_link
. For more information, see Build a custom navigation menu with st.page_link
.
99 |
100 | If you need to programmatically switch pages, use st.switch_page
.
101 |
102 | Users can also navigate between pages using URLs as noted above. When multiple files have the same URL pathname, Streamlit picks the first one (based on the ordering in the navigation menu. Users can view a specific page by visiting the page's URL.
103 |
104 |
105 | Navigating between pages by URL creates a new browser session. In particular, clicking markdown links to other pages resets st.session_state
. In order to retain values in st.session_state
, handle page switching through Streamlit navigation commands and widgets, like st.navigation
, st.switch_page
, st.page_link
, and the built-in navigation menu.
106 |
107 |
108 | If a user tries to access a URL for a page that does not exist, they will see a modal like the one below, saying "Page not found."
109 |
110 | <div style={{ maxWidth: '75%', margin: 'auto' }}>
111 |
112 |
113 |
1 | ---
2 | title: Define multipage apps with st.Page and st.navigation
3 | slug: /develop/concepts/multipage-apps/page-and-navigation
4 | description: Understand the most flexible and preferred method for defining multipage apps
5 | ---
6 |
7 | # Define multipage apps with st.Page
and st.navigation
8 |
9 | st.Page
and st.navigation
are the preferred commands for defining multipage apps. With these commands, you have flexibility to organize your project files and customize your navigation menu. Simply initialize StreamlitPage
objects with st.Page
, then pass those StreamlitPage
objects to st.navigation
in your entrypoint file (i.e. the file you pass to streamlit run
).
10 |
11 | This page assumes you understand the Page terminology presented in the overview.
12 |
13 | ## App structure
14 |
15 | When using st.navigation
, your entrypoint file acts like a page router. Each page is a script executed from your entrypoint file. You can define a page from a Python file or function. If you include elements or widgets in your entrypoint file, they become common elements between your pages. In this case, you can think of your entrypoint file like a picture frame around each of your pages.
16 |
17 | You can only call st.navigation
once per app run and you must call it from your entrypoint file. When a user selects a page in navigation (or is routed through a command like st.switch_page
), st.navigation
returns the selected page. You must manually execute that page with the .run()
method. The following example is a two-page app where each page is defined by a Python file.
18 |
19 | Directory structure:
20 |
21 | 22 | your-repository/ 23 | ├── page_1.py 24 | ├── page_2.py 25 | └── streamlit_app.py 26 |
27 |
28 | streamlit_app.py
:
29 |
30 | python 31 | import streamlit as st 32 | 33 | pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")]) 34 | pg.run() 35 |
36 |
37 | ## Defining pages
38 |
39 | st.Page
lets you define a page. The first and only required argument defines your page source, which can be a Python file or function. When using Python files, your pages may be in a subdirectory (or superdirectory). The path to your page file must always be relative to the entrypoint file. Once you create your page objects, pass them to st.navigation
to register them as pages in your app.
40 |
41 | If you don't define your page title or URL pathname, Streamlit will infer them from the file or function name as described in the multipage apps Overview. However, st.Page
lets you configure them manually. Within st.Page
, Streamlit uses title
to set the page label and title. Additionaly, Streamlit uses icon
to set the page icon and favicon. If you want to have a different page title and label, or different page icon and favicon, you can use st.set_page_config
to change the page title and/or favicon. Just call st.set_page_config
after st.navigation
, either in your entrypoint file or in your page source.
42 |
43 | The following example uses st.set_page_config
to set a page title and favicon consistently across pages. Each page will have its own label and icon in the navigation menu, but the browser tab will show a consistent title and favicon on all pages.
44 |
45 | Directory structure:
46 |
47 | 48 | your-repository/ 49 | ├── create.py 50 | ├── delete.py 51 | └── streamlit_app.py 52 |
53 |
54 | streamlit_app.py
:
55 |
56 | python 57 | import streamlit as st 58 | 59 | create_page = st.Page("create.py", title="Create entry", icon=":material/add_circle:") 60 | delete_page = st.Page("delete.py", title="Delete entry", icon=":material/delete:") 61 | 62 | pg = st.navigation([create_page, delete_page]) 63 | st.set_page_config(page_title="Data manager", page_icon=":material/edit:") 64 | pg.run() 65 |
66 |
67 | <div style={{ maxWidth: '564px', margin: 'auto' }}>
68 |
69 |
70 |
71 | ## Customizing navigation
72 |
73 | If you want to group your pages into sections,
st.navigation
lets you insert headers within your navigation. Alternatively, you can disable the default navigation widget and build a custom navigation menu with st.page_link
.
74 |
75 | Additionally, you can dynamically change which pages you pass to st.navigation
. However, only the page returned by st.navigation
accepts the .run()
method. If a user enters a URL with a pathname, and that pathname is not associated to a page in st.navigation
(on first run), Streamlit will throw a "Page not found" error and redirect them to the default page.
76 |
77 | ### Adding section headers
78 |
79 | As long as you don't want to hide a valid, accessible page in the navigation menu, the simplest way to customize your navigation menu is to organize the pages within st.navigation
. You can sort or group pages, as well as remove any pages you don't want the user to access. This is a convenient way to handle user permissions.
80 |
81 | The following example creates two menu states. When a user starts a new session, they are not logged in. In this case, the only available page is the login page. If a user tries to access another page by URL, it will create a new session and Streamlit will not recognize the page. The user will be diverted to the login page. However, after a user logs in, they will see a navigation menu with three sections and be directed to the dashboard as the app's default page (i.e. homepage).
82 |
83 | Directory structure:
84 |
85 | 86 | your-repository/ 87 | ├── reports 88 | │ ├── alerts.py 89 | │ ├── bugs.py 90 | │ └── dashboard.py 91 | ├── tools 92 | │ ├── history.py 93 | │ └── search.py 94 | └── streamlit_app.py 95 |
96 |
97 | streamlit_app.py
:
98 |
99 | python 100 | import streamlit as st 101 | 102 | if "logged_in" not in st.session_state: 103 | st.session_state.logged_in = False 104 | 105 | def login(): 106 | if st.button("Log in"): 107 | st.session_state.logged_in = True 108 | st.rerun() 109 | 110 | def logout(): 111 | if st.button("Log out"): 112 | st.session_state.logged_in = False 113 | st.rerun() 114 | 115 | login_page = st.Page(login, title="Log in", icon=":material/login:") 116 | logout_page = st.Page(logout, title="Log out", icon=":material/logout:") 117 | 118 | dashboard = st.Page( 119 | "reports/dashboard.py", title="Dashboard", icon=":material/dashboard:", default=True 120 | ) 121 | bugs = st.Page("reports/bugs.py", title="Bug reports", icon=":material/bug_report:") 122 | alerts = st.Page( 123 | "reports/alerts.py", title="System alerts", icon=":material/notification_important:" 124 | ) 125 | 126 | search = st.Page("tools/search.py", title="Search", icon=":material/search:") 127 | history = st.Page("tools/history.py", title="History", icon=":material/history:") 128 | 129 | if st.session_state.logged_in: 130 | pg = st.navigation( 131 | { 132 | "Account": [logout_page], 133 | "Reports": [dashboard, bugs, alerts], 134 | "Tools": [search, history], 135 | } 136 | ) 137 | else: 138 | pg = st.navigation([login_page]) 139 | 140 | pg.run() 141 |
142 |
143 | <div style={{ maxWidth: '564px', margin: 'auto' }}>
144 |
145 |
146 |
147 | ### Dynamically changing the available pages
148 |
149 | You can change what pages are available to a user by updating the list of pages in
st.navigation
. This is a convenient way to handle role-based or user-based access to certain pages. For more information, check out our tutorial, Create a dynamic navigation menu.
150 |
151 | ### Building a custom navigation menu
152 |
153 | If you want more control over your navigation menu, you can hide the default navigation and build your own. You can hide the default navigation by including position="hidden"
in your st.navigation
command. If you want a page to be available to a user without showing it in the navigation menu, you must use this method. A user can't be routed to a page if the page isn't included in st.navigation
. This applies to navigation by URL as well as commands like st.switch_page
and st.page_link
.
154 |
1 | ---
2 | title: Creating multipage apps using the pages/
directory
3 | slug: /develop/concepts/multipage-apps/pages-directory
4 | description: Streamlit provides a simple way to create multipage apps.
5 | ---
6 |
7 | # Creating multipage apps using the pages/
directory
8 |
9 | The most customizable method for declaring multipage apps is using Page and navigation. However, Streamlit also provides a frictionless way to create multipage apps where pages are automatically recognized and shown in a navigation widget inside your app's sidebar. This method uses the pages/
directory.
10 |
11 | This page assumes you understand the Page terminology presented in the overview.
12 |
13 | ## App structure
14 |
15 | When you use the pages/
directory, Streamlit identifies pages in your multipage app by directory structure and filenames. Your entrypoint file (the file you pass to streamlit run
), is your app's homepage. When you have a pages/
directory next to your entrypoint file, Streamlit will identify each Python file within it as a page. The following example has three pages. your_homepage.py
is the entrypoint file and homepage.
16 |
17 | 18 | your_working_directory/ 19 | ├── pages/ 20 | │ ├── a_page.py 21 | │ └── another_page.py 22 | └── your_homepage.py 23 |
24 |
25 | Run your multipage app just like you would for a single-page app. Pass your entrypoint file to streamlit run
.
26 |
27 | 28 | streamlit run your_homepage.py 29 |
30 |
31 | Only .py
files in the pages/
directory will be identified as pages. Streamlit ignores all other files in the pages/
directory and its subdirectories. Streamlit also ignores Python files in subdirectories of pages/
.
32 |
33 |
34 |
35 | If you call st.navigation
in your app (in any session), Streamlit will switch to using the newer, Page-and-navigation multipage structure. In this case, the pages/
directory will be ignored across all sessions. You will not be able to revert back to the pages/
directory unless you restart you app.
36 |
37 |
38 |
39 | ### How pages are sorted in the sidebar
40 |
41 | See the overview to understand how Streamlit assigns Automatic page labels and URLs based on the number
, separator
, identifier
, and ".py"
extension that constitute a filename.
42 |
43 | The entrypoint file is always displayed first. The remaining pages are sorted as follows:
44 |
45 | - Files that have a number
appear before files without a number
.
46 | - Files are sorted based on the number
(if any), followed by the label
(if any).
47 | - When files are sorted, Streamlit treats the number
as an actual number rather than a string. So 03
is the same as 3
.
48 |
49 | This table shows examples of filenames and their corresponding labels, sorted by the order in which they appear in the sidebar.
50 |
51 | Examples:
52 |
53 | | Filename | Rendered label |
54 | | :------------------------ | :----------------- |
55 | | 1 - first page.py
| first page |
56 | | 12 monkeys.py
| monkeys |
57 | | 123.py
| 123 |
58 | | 123_hello_dear_world.py
| hello dear world |
59 | | _12 monkeys.py
| 12 monkeys |
60 |
61 |
62 |
63 | Emojis can be used to make your page names more fun! For example, a file named 🏠_Home.py
will create a page titled "🏠 Home" in the sidebar. When adding emojis to filenames, it’s best practice to include a numbered prefix to make autocompletion in your terminal easier. Terminal-autocomplete can get confused by unicode (which is how emojis are represented).
64 |
65 |
66 |
67 | ## Notes and limitations
68 |
69 | - Pages support run-on-save.
70 | - When you update a page while your app is running, this causes a rerun for users currently viewing that exact page.
71 | - When you update a page while your app is running, the app will not automatically rerun for users currently viewing a different page.
72 | - While your app is running, adding or deleting a page updates the sidebar navigation immediately.
73 | - st.set_page_config
works at the page level.
74 | - When you set title
or favicon
using st.set_page_config
, this applies to the current page only.
75 | - When you set layout
using st.set_page_config
, the setting will remain for the session until changed by another call to st.set_page_config
. If you use st.set_page_config
to set layout
, it's recommended to call it on all pages.
76 | - Pages share the same Python modules globally:
77 |
78 | python 79 | # page1.py 80 | import foo 81 | foo.hello = 123 82 | 83 | # page2.py 84 | import foo 85 | st.write(foo.hello) # If page1 already executed, this writes 123 86 |
87 |
88 | - Pages share the same st.session_state:
89 |
90 | python 91 | # page1.py 92 | import streamlit as st 93 | if "shared" not in st.session_state: 94 | st.session_state["shared"] = True 95 | 96 | # page2.py 97 | import streamlit as st 98 | st.write(st.session_state["shared"]) # If page1 already executed, this writes True 99 |
100 |
101 | You now have a solid understanding of multipage apps. You've learned how to structure apps, define pages, and navigate between pages in the user interface. It's time to create your first multipage app! 🥳
102 |
1 | ---
2 | title: Working with widgets in multipage apps
3 | slug: /develop/concepts/multipage-apps/widgets
4 | description: Understand how widgets interact with pages
5 | ---
6 |
7 | # Working with widgets in multipage apps
8 |
9 | When you create a widget in a Streamlit app, Streamlit generates a widget ID and uses it to make your widget stateful. As your app reruns with user interaction, Streamlit keeps track of the widget's value by associating its value to its ID. In particular, a widget's ID depends on the page where it's created. If you define an identical widget on two different pages, then the widget will reset to its default value when you switch pages.
10 |
11 | This guide explains three strategies to deal with the behavior if you'd like to have a widget remain stateful across all pages. If don't want a widget to appear on all pages, but you do want it to remain stateful when you navigate away from its page (and then back), Options 2 and 3 can be used. For detailed information about these strategies, see Understanding widget behavior.
12 |
13 | ## Option 1 (preferred): Execute your widget command in your entrypoint file
14 |
15 | When you define your multipage app with st.Page
and st.navigation
, your entrypoint file becomes a frame of common elements around your pages. When you execute a widget command in your entrypoint file, Streamlit associates the widget to your entrypoint file instead of a particular page. Since your entrypoint file is executed in every app rerun, any widget in your entrypoint file will remain stateful as your users switch between pages.
16 |
17 | This method does not work if you define your app with the pages/
directory.
18 |
19 | The following example includes a selectbox and slider in the sidebar that are rendered and stateful on all pages. The widgets each have an assigned key so you can access their values through Session State within a page.
20 |
21 | Directory structure:
22 |
23 | 24 | your-repository/ 25 | ├── page_1.py 26 | ├── page_2.py 27 | └── streamlit_app.py 28 |
29 |
30 | streamlit_app.py
:
31 |
32 | python 33 | import streamlit as st 34 | 35 | pg = st.navigation([st.Page("page_1.py"), st.Page("page_2.py")]) 36 | 37 | st.sidebar.selectbox("Group", ["A","B","C"], key="group") 38 | st.sidebar.slider("Size", 1, 5, key="size") 39 | 40 | pg.run() 41 |
42 |
43 | ## Option 2: Save your widget values into a dummy key in Session State
44 |
45 | If you want to navigate away from a widget and return to it while keeping its value, or if you want to use the same widget on multiple pages, use a separate key in st.session_state
to save the value independently from the widget. In this example, a temporary key is used with a widget. The temporary key uses an underscore prefix. Hence, "_my_key"
is used as the widget key, but the data is copied to "my_key"
to preserve it between pages.
46 |
47 | python 48 | import streamlit as st 49 | 50 | def store_value(): 51 | # Copy the value to the permanent key 52 | st.session_state["my_key"] = st.session_state["_my_key"] 53 | 54 | # Copy the saved value to the temporary key 55 | st.session_state["_my_key"] = st.session_state["my_key"] 56 | st.number_input("Number of filters", key="_my_key", on_change=store_value) 57 |
58 |
59 | If this is functionalized to work with multiple widgets, it could look something like this:
60 |
61 | python 62 | import streamlit as st 63 | 64 | def store_value(key): 65 | st.session_state[key] = st.session_state["_"+key] 66 | def load_value(key): 67 | st.session_state["_"+key] = st.session_state[key] 68 | 69 | load_value("my_key") 70 | st.number_input("Number of filters", key="_my_key", on_change=store_value, args=["my_key"]) 71 |
72 |
73 | ## Option 3: Interrupt the widget clean-up process
74 |
75 | When Streamlit gets to the end of an app run, it will delete the data for any widgets that were not rendered. This includes data for any widget not associated to the current page. However, if you re-save a key-value pair in an app run, Streamlit will not associate the key-value pair to any widget until you execute a widget command again with that key.
76 |
77 | As a result, if you have the following code at the top of every page, any widget with the key "my_key"
will retain its value wherever it's rendered (or not). Alternatively, if you are using st.navigation
and st.Page
, you can include this once in your entrypoint file before executing your page.
78 |
79 | python 80 | if "my_key" in st.session_state: 81 | st.session_state.my_key = st.session_state.my_key 82 |
83 |
1 | --- 2 | title: Quick reference 3 | slug: /develop/quick-reference 4 | --- 5 | 6 | # Quick reference 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | A dense list of Streamlit commands with example syntax. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | See how Streamlit has changed with each new version. 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Understand how we introduce new features and how you can get your hands on them sooner! 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Get a sneak peek at what we have scheduled for the next year. 39 | 40 | 41 | 42 | 43 |1 | ---
2 | title: Streamlit API cheat sheet
3 | slug: /develop/quick-reference/cheat-sheet
4 | ---
5 |
6 | # Streamlit API cheat sheet
7 |
8 | This is a summary of the docs for the latest version of Streamlit, v1.45.0.
9 |
10 |
11 |
12 |
13 |
14 | #### Install & Import
15 |
16 | python 17 | pip install streamlit 18 | 19 | streamlit run first_app.py 20 | 21 | # Import convention 22 | >>> import streamlit as st 23 |
24 |
25 |
26 |
27 |
28 |
29 | #### Pre-release features
30 |
31 | python 32 | pip uninstall streamlit 33 | pip install streamlit-nightly --upgrade 34 |
35 |
36 | Learn more about experimental features
37 |
38 |
39 |
40 |
41 |
42 | #### Command line
43 |
44 | python 45 | streamlit cache clear 46 | streamlit config show 47 | streamlit docs 48 | streamlit hello 49 | streamlit help 50 | streamlit init 51 | streamlit run streamlit_app.py 52 | streamlit version 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | #### Magic commands
64 |
65 | python 66 | # Magic commands implicitly 67 | # call st.write(). 68 | "_This_ is some **Markdown**" 69 | my_variable 70 | "dataframe:", my_data_frame 71 | 72 |
73 |
74 |
75 |
76 |
77 |
78 | #### Display text
79 |
80 | python 81 | st.write("Most objects") # df, err, func, keras! 82 | st.write(["st", "is <", 3]) 83 | st.write_stream(my_generator) 84 | st.write_stream(my_llm_stream) 85 | 86 | st.text("Fixed width text") 87 | st.markdown("_Markdown_") 88 | st.latex(r""" e^{i\pi} + 1 = 0 """) 89 | st.title("My title") 90 | st.header("My header") 91 | st.subheader("My sub") 92 | st.code("for i in range(8): foo()") 93 | st.badge("New") 94 | st.html("<p>Hi!</p>") 95 |
96 |
97 |
98 |
99 |
100 |
101 | #### Display data
102 |
103 | python 104 | st.dataframe(my_dataframe) 105 | st.table(data.iloc[0:10]) 106 | st.json({"foo":"bar","fu":"ba"}) 107 | st.metric("My metric", 42, 2) 108 | 109 |
110 |
111 |
112 |
113 |
114 |
115 | #### Display media
116 |
117 | python 118 | st.image("./header.png") 119 | st.audio(data) 120 | st.video(data) 121 | st.video(data, subtitles="./subs.vtt") 122 | st.logo("logo.jpg") 123 |
124 |
125 |
126 |
127 |
128 |
129 | #### Display charts
130 |
131 | python 132 | st.area_chart(df) 133 | st.bar_chart(df) 134 | st.bar_chart(df, horizontal=True) 135 | st.line_chart(df) 136 | st.map(df) 137 | st.scatter_chart(df) 138 | 139 | st.altair_chart(chart) 140 | st.bokeh_chart(fig) 141 | st.graphviz_chart(fig) 142 | st.plotly_chart(fig) 143 | st.pydeck_chart(chart) 144 | st.pyplot(fig) 145 | st.vega_lite_chart(df, spec) 146 | 147 | # Work with user selections 148 | event = st.plotly_chart( 149 | df, 150 | on_select="rerun" 151 | ) 152 | event = st.altair_chart( 153 | chart, 154 | on_select="rerun" 155 | ) 156 | event = st.vega_lite_chart( 157 | df, 158 | spec, 159 | on_select="rerun" 160 | ) 161 |
162 |
163 | To use newer versions of Bokeh, see our custom component streamlit-bokeh
.
164 |
165 |
166 |
167 |
168 |
169 | #### Add elements to sidebar
170 |
171 | python 172 | # Just add it after st.sidebar: 173 | a = st.sidebar.radio("Select one:", [1, 2]) 174 | 175 | # Or use "with" notation: 176 | with st.sidebar: 177 | st.radio("Select one:", [1, 2]) 178 |
179 |
180 |
181 |
182 |
183 |
184 | #### Columns
185 |
186 | python 187 | # Two equal columns: 188 | col1, col2 = st.columns(2) 189 | col1.write("This is column 1") 190 | col2.write("This is column 2") 191 | 192 | # Three different columns: 193 | col1, col2, col3 = st.columns([3, 1, 1]) 194 | # col1 is larger. 195 | 196 | # Bottom-aligned columns 197 | col1, col2 = st.columns(2, vertical_alignment="bottom") 198 | 199 | # You can also use "with" notation: 200 | with col1: 201 | st.radio("Select one:", [1, 2]) 202 |
203 |
204 |
205 |
206 |
207 |
208 | #### Tabs
209 |
210 | python 211 | # Insert containers separated into tabs: 212 | tab1, tab2 = st.tabs(["Tab 1", "Tab2"]) 213 | tab1.write("this is tab 1") 214 | tab2.write("this is tab 2") 215 | 216 | # You can also use "with" notation: 217 | with tab1: 218 | st.radio("Select one:", [1, 2]) 219 |
220 |
221 |
222 |
223 |
224 |
225 | #### Expandable containers
226 |
227 | python 228 | expand = st.expander("My label", icon=":material/info:") 229 | expand.write("Inside the expander.") 230 | pop = st.popover("Button label") 231 | pop.checkbox("Show all") 232 | 233 | # You can also use "with" notation: 234 | with expand: 235 | st.radio("Select one:", [1, 2]) 236 |
237 |
238 |
239 |
240 |
241 |
242 | #### Control flow
243 |
244 | python 245 | # Stop execution immediately: 246 | st.stop() 247 | # Rerun script immediately: 248 | st.rerun() 249 | # Navigate to another page: 250 | st.switch_page("pages/my_page.py") 251 | 252 | # Define a navigation widget in your entrypoint file 253 | pg = st.navigation( 254 | st.Page("page1.py", title="Home", url_path="home", default=True) 255 | st.Page("page2.py", title="Preferences", url_path="settings") 256 | ) 257 | pg.run() 258 | 259 | # Group multiple widgets: 260 | with st.form(key="my_form"): 261 | username = st.text_input("Username") 262 | password = st.text_input("Password") 263 | st.form_submit_button("Login") 264 | 265 | # Define a dialog function 266 | @st.dialog("Welcome!") 267 | def modal_dialog(): 268 | st.write("Hello") 269 | 270 | modal_dialog() 271 | 272 | # Define a fragment 273 | @st.fragment 274 | def fragment_function(): 275 | df = get_data() 276 | st.line_chart(df) 277 | st.button("Update") 278 | 279 | fragment_function() 280 |
281 |
282 |
283 |
284 |
285 |
286 | #### Display interactive widgets
287 |
288 | python 289 | st.button("Click me") 290 | st.download_button("Download file", data) 291 | st.link_button("Go to gallery", url) 292 | st.page_link("app.py", label="Home") 293 | st.data_editor("Edit data", data) 294 | st.checkbox("I agree") 295 | st.feedback("thumbs") 296 | st.pills("Tags", ["Sports", "Politics"]) 297 | st.radio("Pick one", ["cats", "dogs"]) 298 | st.segmented_control("Filter", ["Open", "Closed"]) 299 | st.toggle("Enable") 300 | st.selectbox("Pick one", ["cats", "dogs"]) 301 | st.multiselect("Buy", ["milk", "apples", "potatoes"]) 302 | st.slider("Pick a number", 0, 100) 303 | st.select_slider("Pick a size", ["S", "M", "L"]) 304 | st.text_input("First name") 305 | st.number_input("Pick a number", 0, 10) 306 | st.text_area("Text to translate") 307 | st.date_input("Your birthday") 308 | st.time_input("Meeting time") 309 | st.file_uploader("Upload a CSV") 310 | st.audio_input("Record a voice message") 311 | st.camera_input("Take a picture") 312 | st.color_picker("Pick a color") 313 | 314 | # Use widgets' returned values in variables: 315 | for i in range(int(st.number_input("Num:"))): 316 | foo() 317 | if st.sidebar.selectbox("I:",["f"]) == "f": 318 | b() 319 | my_slider_val = st.slider("Quinn Mallory", 1, 88) 320 | st.write(slider_val) 321 | 322 | # Disable widgets to remove interactivity: 323 | st.slider("Pick a number", 0, 100, disabled=True) 324 |
325 |
326 |
327 |
328 |
329 |
330 | #### Build chat-based apps
331 |
332 | python 333 | # Insert a chat message container. 334 | with st.chat_message("user"): 335 | st.write("Hello 👋") 336 | st.line_chart(np.random.randn(30, 3)) 337 | 338 | # Display a chat input widget at the bottom of the app. 339 | st.chat_input("Say something") 340 | 341 | # Display a chat input widget inline. 342 | with st.container(): 343 | st.chat_input("Say something") 344 |
345 |
346 | Learn how to Build a basic LLM chat app
347 |
348 |
349 |
350 |
351 |
352 | #### Mutate data
353 |
354 | python 355 | # Add rows to a dataframe after 356 | # showing it. 357 | element = st.dataframe(df1) 358 | element.add_rows(df2) 359 | 360 | # Add rows to a chart after 361 | # showing it. 362 | element = st.line_chart(df1) 363 | element.add_rows(df2) 364 |
365 |
366 |
367 |
368 |
369 |
370 | #### Display code
371 |
372 | python 373 | with st.echo(): 374 | st.write("Code will be executed and printed") 375 |
376 |
377 |
378 |
379 |
380 |
381 | #### Placeholders, help, and options
382 |
383 | python 384 | # Replace any single element. 385 | element = st.empty() 386 | element.line_chart(...) 387 | element.text_input(...) # Replaces previous. 388 | 389 | # Insert out of order. 390 | elements = st.container() 391 | elements.line_chart(...) 392 | st.write("Hello") 393 | elements.text_input(...) # Appears above "Hello". 394 | 395 | st.help(pandas.DataFrame) 396 | st.get_option(key) 397 | st.set_option(key, value) 398 | st.set_page_config(layout="wide") 399 | st.query_params[key] 400 | st.query_params.from_dict(params_dict) 401 | st.query_params.get_all(key) 402 | st.query_params.clear() 403 | st.html("<p>Hi!</p>") 404 |
405 |
406 |
407 |
408 |
409 |
410 | #### Connect to data sources
411 |
412 | python 413 | st.connection("pets_db", type="sql") 414 | conn = st.connection("sql") 415 | conn = st.connection("snowflake") 416 | 417 | class MyConnection(BaseConnection[myconn.MyConnection]): 418 | def _connect(self, **kwargs) -> MyConnection: 419 | return myconn.connect(**self._secrets, **kwargs) 420 | def query(self, query): 421 | return self._instance.query(query) 422 |
423 |
424 |
425 |
426 |
427 |
428 | #### Optimize performance
429 |
430 | ###### Cache data objects
431 |
432 | python 433 | # E.g. Dataframe computation, storing downloaded data, etc. 434 | @st.cache_data 435 | def foo(bar): 436 | # Do something expensive and return data 437 | return data 438 | # Executes foo 439 | d1 = foo(ref1) 440 | # Does not execute foo 441 | # Returns cached item by value, d1 == d2 442 | d2 = foo(ref1) 443 | # Different arg, so function foo executes 444 | d3 = foo(ref2) 445 | # Clear the cached value for foo(ref1) 446 | foo.clear(ref1) 447 | # Clear all cached entries for this function 448 | foo.clear() 449 | # Clear values from *all* in-memory or on-disk cached functions 450 | st.cache_data.clear() 451 |
452 |
453 | ###### Cache global resources
454 |
455 | python 456 | # E.g. TensorFlow session, database connection, etc. 457 | @st.cache_resource 458 | def foo(bar): 459 | # Create and return a non-data object 460 | return session 461 | # Executes foo 462 | s1 = foo(ref1) 463 | # Does not execute foo 464 | # Returns cached item by reference, s1 == s2 465 | s2 = foo(ref1) 466 | # Different arg, so function foo executes 467 | s3 = foo(ref2) 468 | # Clear the cached value for foo(ref1) 469 | foo.clear(ref1) 470 | # Clear all cached entries for this function 471 | foo.clear() 472 | # Clear all global resources from cache 473 | st.cache_resource.clear() 474 |
475 |
476 |
477 |
478 |
479 |
480 | #### Display progress and status
481 |
482 | python 483 | # Show a spinner during a process 484 | with st.spinner(text="In progress"): 485 | time.sleep(3) 486 | st.success("Done") 487 | 488 | # Show and update progress bar 489 | bar = st.progress(50) 490 | time.sleep(3) 491 | bar.progress(100) 492 | 493 | with st.status("Authenticating...") as s: 494 | time.sleep(2) 495 | st.write("Some long response.") 496 | s.update(label="Response") 497 | 498 | st.balloons() 499 | st.snow() 500 | st.toast("Warming up...") 501 | st.error("Error message") 502 | st.warning("Warning message") 503 | st.info("Info message") 504 | st.success("Success message") 505 | st.exception(e) 506 |
507 |
508 |
509 |
510 |
511 |
512 | #### Personalize apps for users
513 |
514 | python 515 | # Authenticate users 516 | if not st.user.is_logged_in: 517 | st.login("my_provider") 518 | f"Hi, {st.user.name}" 519 | st.logout() 520 | 521 | # Get dictionaries of cookies, headers, locale, and browser data 522 | st.context.cookies 523 | st.context.headers 524 | st.context.ip_address 525 | st.context.is_embedded 526 | st.context.locale 527 | st.context.timezone 528 | st.context.timezone_offset 529 | st.context.url 530 |
531 |
532 |
533 |
534 |
1 | ---
2 | title: Pre-release features
3 | slug: /develop/quick-reference/prerelease
4 | ---
5 |
6 | # Pre-release features
7 |
8 | At Streamlit, we like to move quick while keeping things stable. In our latest effort to move even faster without sacrificing stability, we're offering our bold and fearless users two ways to try out Streamlit's bleeding-edge features:
9 |
10 | 1. Experimental features
11 | 2. Nightly releases
12 |
13 | ## Experimental Features
14 |
15 | Less stable Streamlit features have one naming convention: st.experimental_
. This distinction is a prefix we attach to our command names to make sure their status is clear to everyone.
16 |
17 | Here's a quick rundown of what you get from each naming convention:
18 |
19 | - st: this is where our core features like st.write
and st.dataframe
live. If we ever make backward-incompatible changes to these, they will take place gradually and with months of announcements and warnings.
20 | - experimental: this is where we'll put all new features that may or may not ever make it into Streamlit core. This gives you a chance to try the next big thing we're cooking up weeks or months before we're ready to stabilize its API. We don't know whether these features have a future, but we want you to have access to everything we're trying, and work with us to figure them out.
21 |
22 | Features with the experimental_
naming convention are things that we're still working on or trying
23 | to understand. If these features are successful, at some point they'll become part of Streamlit
24 | core. If unsuccessful, these features are removed without much notice. While in experimental, a feature's API and behaviors may not be stable, and it's possible they could change in ways that aren't backward-compatible.
25 |
26 |
27 |
28 | Experimental features and their APIs may change or be removed at any time.
29 |
30 |
31 |
32 | ### The lifecycle of an experimental feature
33 |
34 | 1. A feature is added with the experimental_
prefix.
35 | 2. The feature is potentially tweaked over time, with possible API/behavior breakages.
36 | 3. If successful, we promote the feature to Streamlit core and remove it from experimental_
:
37 | - a. The feature's API stabilizes and the feature is cloned without the experimental_
prefix, so it exists as both st
and experimental_
. At this point, users will see a warning when using the version of the feature with the experimental_
prefix -- but the feature will still work.
38 | - b. At some point, the code of the experimental_
-prefixed feature is removed, but there will still be a stub of the function prefixed with experimental_
that shows an error with appropriate instructions.
39 | - c. Finally, at a later date the experimental_
version is removed.
40 | 4. If unsuccessful, the feature is removed without much notice and we leave a stub in experimental_
that shows an error with instructions.
41 |
42 | ## Nightly releases
43 |
44 | In addition to experimental features, we offer another way to try out Streamlit's newest features: nightly releases.
45 |
46 | At the end of each day (at night 🌛), our bots run automated tests against the latest Streamlit code and, if everything looks good, it publishes them as the streamlit-nightly
package. This means the nightly build includes all our latest features, bug fixes, and other enhancements on the same day they land on our codebase.
47 |
48 | How does this differ from official releases?
49 |
50 | Official Streamlit releases go not only through both automated tests but also rigorous manual testing, while nightly releases only have automated tests. It's important to keep in mind that new features introduced in nightly releases often lack polish. In our official releases, we always make double-sure all new features are ready for prime time.
51 |
52 | How do I use the nightly release?
53 |
54 | All you need to do is install the streamlit-nightly
package:
55 |
56 | bash 57 | pip uninstall streamlit 58 | pip install streamlit-nightly --upgrade 59 |
60 |
61 |
62 |
63 | You should never have both streamlit
and streamlit-nightly
installed in the same environment!
64 |
65 |
66 |
67 | Why should I use the nightly release?
68 |
69 | Because you can't wait for official releases, and you want to help us find bugs early!
70 |
71 | Why shouldn't I use the nightly release?
72 |
73 | While our automated tests have high coverage, there's still a significant likelihood that there will be some bugs in the nightly code.
74 |
75 | Can I choose which nightly release I want to install?
76 |
77 | If you'd like to use a specific version, you can find the version number in our Release history. Specify the desired version using pip
as usual: pip install streamlit-nightly==x.yy.zz-123456
.
78 |
79 | Can I compare changes between releases?
80 |
81 | If you'd like to review the changes for a nightly release, you can use the comparison tool on GitHub.
82 |
1 | ---
2 | title: 2019 release notes
3 | slug: /develop/quick-reference/release-notes/2019
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2019 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2019. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 0.52.0
13 |
14 | Release date: December 20, 2019
15 |
16 | Highlights:
17 |
18 | - 📤 Preview release of the file uploader widget. To try it out just call
19 | st.file_uploader
!
20 |
21 | Note that as a preview release things may change in the near future.
22 | Looking forward to hearing input from the community before we stabilize the
23 | API!
24 |
25 | - 👋 Support for emoji codes in
26 | st.write
and st.markdown
! Try it out with st.write("Hello :wave:")
.
27 |
28 | Breaking changes:
29 |
30 | - 🧹 st.pyplot
now clears figures by default, since that's what you want 99% of
31 | the time. This allows you to create two or more Matplotlib charts without
32 | having to call
33 | pyplot.clf
34 | every time. If you want to turn this behavior off, use
35 | st.pyplot(clear_figure=False)
36 | - 📣 st.cache
no longer checks for input mutations. This is the first change
37 | of our ongoing effort to simplify the caching system and prepare Streamlit
38 | for the launch of other caching primitives like Session State!
39 |
40 | ## Version 0.51.0
41 |
42 | Release date: November 30, 2019
43 |
44 | Highlights:
45 |
46 | - 🐕 You can now tweak the behavior of the file watcher with the config option server.fileWatcherType
. Use it to switch between:
47 | - auto
(default) : Streamlit will attempt to use the watchdog module, and
48 | falls back to polling if watchdog is not available.
49 | - watchdog
: Force Streamlit to use the watchdog module.
50 | - poll
: Force Streamlit to always use polling.
51 | - none
: Streamlit will not watch files.
52 |
53 | Notable bug fixes:
54 |
55 | - Fix the "keyPrefix" option in static report sharing #724
56 | - Add support for getColorX and getTargetColorX to DeckGL Chart #718
57 | - Fixing Tornado on Windows + Python 3.8 #682
58 | - Fall back on webbrowser if xdg-open is not installed on Linux #701
59 | - Fixing number input spin buttons for Firefox #683
60 | - Fixing CTRL+ENTER on Windows #699
61 | - Do not automatically create credential file when in headless mode #467
62 |
63 | ## Version 0.50.1
64 |
65 | Release date: November 10, 2019
66 |
67 | Highlights:
68 |
69 | - 👩🎓 SymPy support and ability to draw mathematical expressions using LaTeX! See
70 | st.latex
,
71 | st.markdown
,
72 | and
73 | st.write
.
74 | - 🌄 You can now set config options using environment variables. For example,
75 | export STREAMLIT_SERVER_PORT=9876
.
76 | - 🐱 Ability to call streamlit run
directly with Github and Gist URLs. No
77 | need to grab the "raw" URL first!
78 | - 📃 Cleaner exception stack traces. We now remove all Streamlit-specific code
79 | from stack traces originating from the user's app.
80 |
81 | ## Version 0.49.0
82 |
83 | Release date: October 23, 2019
84 |
85 | Highlights:
86 |
87 | - 💯 New input widget for entering numbers with the keyboard: st.number_input()
88 | - 📺 Audio/video improvements: ability to load from a URL, to embed YouTube
89 | videos, and to set the start position.
90 | - 🤝 You can now (once again) share static snapshots of your apps to S3! See
91 | the S3 section of streamlit config show
to set it up. Then share from
92 | top-right menu.
93 | - ⚙️ Use server.baseUrlPath
config option to set Streamlit's URL to something
94 | like http://domain.com/customPath
.
95 |
96 | Notable bug fixes:
97 |
98 | - Fixes numerous Windows bugs, including Issues
99 | #339 and
100 | #401.
101 |
102 | ## Version 0.48.0
103 |
104 | Release date: October 12, 2019
105 |
106 | Highlights:
107 |
108 | - 🔧 Ability to set config options as command line flags or in a local config file.
109 | - @st.cache
hashing.
112 | - 🎛️ Improved handling of widget state when Python file is modified.
113 | - 🙈 Improved HTML support in st.write
and st.markdown
. HTML is still unsafe, though!
114 |
115 | Notable bug fixes:
116 |
117 | - Fixes @st.cache
bug related to having your Python environment on current
118 | working directory. Issue #242
119 | - Fixes loading of root url /
on Windows. Issue #244
120 |
121 | ## Version 0.47.0
122 |
123 | Release date: October 1, 2019
124 |
125 | Highlights:
126 |
127 | - 🌄 New hello.py showing off 4 glorious Streamlit apps. Try it out!
128 | - 🔄 Streamlit now automatically selects an unused port when 8501 is already in use.
129 | - 🎁 Sidebar support is now out of beta! Just start any command with st.sidebar.
instead of st.
130 | - ⚡ Performance improvements: we added a cache to our websocket layer so we no longer re-send data to the browser when it hasn't changed between runs
131 | - 📈 Our "native" charts st.line_chart
, st.area_chart
and st.bar_chart
now use Altair behind the scenes
132 | - 🔫 Improved widgets: custom st.slider labels; default values in multiselect
133 | - 🕵️♀️ The filesystem watcher now ignores hidden folders and virtual environments
134 | - 💅 Plus lots of polish around caching and widget state management
135 |
136 | Breaking change:
137 |
138 | - 🛡️ We have temporarily disabled support for sharing static "snapshots" of Streamlit apps. Now that we're no longer in a limited-access beta, we need to make sure sharing is well thought through and abides by laws like the DMCA. But we're working on a solution!
139 |
140 | ## Version 0.46.0
141 |
142 | Release date: September 19, 2019
143 |
144 | Highlights:
145 |
146 | - ✨ Magic commands! Use st.write
without typing st.write
. See
147 | https://docs.streamlit.io/en/latest/api.html#magic-commands
148 | - 🎛️ New st.multiselect
widget.
149 | - 🐍 Fixed numerous install issues so now you can use pip install streamlit
150 | even in Conda! We've therefore deactivated our Conda repo.
151 | - 🐞 Multiple bug fixes and additional polish in preparation for our launch!
152 |
153 | Breaking change:
154 |
155 | - 🛡️ HTML tags are now blacklisted in st.write
/st.markdown
by default. More
156 | information and a temporary work-around at:
157 | streamlit/streamlit#152
158 |
159 | ## Version 0.45.0
160 |
161 | Release date: August 28, 2019
162 |
163 | Highlights:
164 |
165 | - 😱 Experimental support for sidebar! Let us know if you want to be a beta
166 | tester.
167 | - 🎁 Completely redesigned st.cache
! Much more performant, has a cleaner API,
168 | support for caching functions called by @st.cached
functions,
169 | user-friendly error messages, and much more!
170 | - 🖼️ Lightning fast st.image
, ability to choose between JPEG and PNG
171 | compression, and between RGB and BGR (for OpenCV).
172 | - 💡 Smarter API for st.slider
, st.selectbox
, and st.radio
.
173 | - 🤖 Automatically fixes the Matplotlib backend -- no need to edit .matplotlibrc
174 |
175 | ## Version 0.44.0
176 |
177 | Release date: July 28, 2019
178 |
179 | Highlights:
180 |
181 | - ⚡ Lightning-fast reconnect when you do a ctrl-c/rerun on your Streamlit code
182 | - 📣 Useful error messages when the connection fails
183 | - 💎 Fixed multiple bugs and improved polish of our newly-released interactive widgets
184 |
185 | ## Version 0.43.0
186 |
187 | Release date: July 9, 2019
188 |
189 | Highlights:
190 |
191 | - ⚡ Support for interactive widgets! 🎈🎉
192 |
193 | ## Version 0.42.0
194 |
195 | Release date: July 1, 2019
196 |
197 | Highlights:
198 |
199 | - 💾 Ability to save Vega-Lite and Altair charts to SVG or PNG
200 | - 🐇 We now cache JS files in your browser for faster loading
201 | - ⛔ Improvements to error-handling inside Streamlit apps
202 |
203 | ## Version 0.41.0
204 |
205 | Release date: June 24, 2019
206 |
207 | Highlights:
208 |
209 | - 📈 Greatly improved our support for named datasets in Vega-Lite and Altair
210 | - 🙄 Added ability to ignore certain folders when watching for file changes. See the server.folderWatchBlacklist
config option.
211 | - ☔ More robust against syntax errors on the user's script and imported modules
212 |
213 | ## Version 0.40.0
214 |
215 | Release date: June 10, 2019
216 |
217 | Highlights:
218 |
219 | - Streamlit is more than 10x faster. Just save and watch your analyses update instantly.
220 | - We changed how you run Streamlit apps:
221 | $ streamlit run your_script.py [script args]
222 | - Unlike the previous versions of Streamlit, streamlit run [script] [script args]
creates a server (now you don't need to worry if the proxy is up). To kill the server, all you need to do is hit Ctrl+c.
223 |
224 | Why is this so much faster?
225 |
226 | Now, Streamlit keeps a single Python session running until you kill the server. This means that Streamlit can re-run your code without kicking off a new process; imported libraries are cached to memory. An added bonus is that st.cache
now caches to memory instead of to disk.
227 |
228 | What happens if I run Streamlit the old way?
229 |
230 | If you run $ python your_script.py
the script will execute from top to bottom, but won't produce a Streamlit app.
231 |
232 | What are the limitations of the new architecture?
233 |
234 | - To switch Streamlit apps, first you have to kill the Streamlit server with Ctrl-c. Then, you can use streamlit run
to generate the next app.
235 | - Streamlit only works when used inside Python files, not interactively from the Python REPL.
236 |
237 | What else do I need to know?
238 |
239 | - The strings we print to the command line when liveSave is on have been cleaned up. You may need to adjust any RegEx that depends on those.
240 | - A number of config options have been renamed:
241 |
242 | | Old config | New config |
243 | | -------------------------- | --------------------- |
244 | | proxy.isRemote | server.headless |
245 | | proxy.liveSave | server.liveSave |
246 | | proxy.runOnSave | server.runOnSave |
247 | | proxy.watchFileSystem | server.runOnSave |
248 | | proxy.enableCORS | server.enableCORS |
249 | | proxy.port | server.port |
250 | | browser.proxyAddress | browser.serverAddress |
251 | | browser.proxyPort | browser.serverPort |
252 | | client.waitForProxySecs | n/a |
253 | | client.throttleSecs | n/a |
254 | | client.tryToOutliveProxy | n/a |
255 | | client.proxyAddress | n/a |
256 | | client.proxyPort | n/a |
257 | | proxy.autoCloseDelaySecs | n/a |
258 | | proxy.reportExpirationSecs | n/a |
259 |
260 | What if something breaks?
261 |
262 | If the new Streamlit isn't working, please let us know by Slack or email. You can downgrade at any time with these commands:
263 |
264 | bash 265 | pip install --upgrade streamlit==0.37 266 |
267 |
268 | bash 269 | conda install streamlit=0.37 270 |
271 |
272 | What's next?
273 |
274 | Thank you for staying with us on this journey! This version of Streamlit lays the foundation for interactive widgets, a new feature of Streamlit we're really excited to share with you in the next few months.
275 |
276 | ## Version 0.36.0
277 |
278 | Release date: May 03, 2019
279 |
280 | Highlights
281 |
282 | - 🚣♀️ st.progress()
now also accepts floats from 0.0–1.0
283 | - 🤯 Improved rendering of long headers in DataFrames
284 | - 🔐 Shared apps now default to HTTPS
285 |
286 | ## Version 0.35.0
287 |
288 | Release date: April 26, 2019
289 |
290 | Highlights
291 |
292 | - 📷 Bokeh support! Check out docs for st.bokeh_chart
293 | - ⚡️ Improved the size and load time of saved apps
294 | - ⚾️ Implemented better error-catching throughout the codebase
295 |
1 | ---
2 | title: 2020 release notes
3 | slug: /develop/quick-reference/release-notes/2020
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2020 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2020. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 0.73.0
13 |
14 | Release date: December 17, 2020
15 |
16 | Notable Changes
17 |
18 | - 🐍 Streamlit can now be installed on Python 3.9. Streamlit components are not
19 | yet compatible with Python 3.9 and must use version 3.8 or earlier.
20 | - 🧱 Streamlit Components now allows same origin, enabling features provided by
21 | the browser such as a webcam component.
22 | - 🐙 Fix Streamlit sharing deploy experience for users running on Git versions
23 | 2.7.0 or earlier.
24 | - 🧰 Handle unexpected closing of uploaded files for st.file_uploader
.
25 |
26 | ## Version 0.72.0
27 |
28 | Release date: December 2, 2020
29 |
30 | Notable Changes
31 |
32 | - 🌈 Establish a framework for theming and migrate existing components.
33 | - 📱 Improve the sidebar experience for mobile devices.
34 | - 🧰 Update st.file_uploader
to reduce reruns.
35 |
36 | ## Version 0.71.0
37 |
38 | Release date: November 11, 2020
39 |
40 | Notable Changes
41 |
42 | - 📁 Updated st.file_uploader
43 | to automatically reset buffer on app reruns.
44 | - 📊 Optimize the default rendering of charts and reduce issues with the initial render.
45 |
46 | ## Version 0.70.0
47 |
48 | Release date: October 28, 2020
49 |
50 | Notable Changes
51 |
52 | - 🧪 st.set_page_config
and st.color_picker
have now been moved into the
53 | Streamlit namespace. These will be removed from beta January 28th, 2021. Learn
54 | more about our beta process here.
55 | - 📊 Improve display of bar charts for discrete values.
56 |
57 | ## Version 0.69.0
58 |
59 | Release date: October 15, 2020
60 |
61 | Highlights:
62 |
63 | - 🎁 Introducing Streamlit sharing, the best way to deploy, manage, and share your public Streamlit apps—for free. Read more about it on our blog post or sign up here!
64 | - Added st.experimental_rerun
to programatically re-run your app. Thanks SimonBiggs!
65 |
66 | Notable Changes
67 |
68 | - 📹 Better support across browsers for start and stop times for st.video.
69 | - 🖼 Bug fix for intermittently failing media files
70 | - 📦 Bug fix for custom components compatibility with Safari. Make sure to upgrade to the latest streamlit-component-lib.
71 |
72 | ## Version 0.68.0
73 |
74 | Release date: October 8, 2020
75 |
76 | Highlights:
77 |
78 | - ⌗ Introducing new layout options for Streamlit! Move aside, vertical layout.
79 | Make a little space for... horizontal layout! Check out our
80 | blog post.
81 | - 💾 File uploader redesigned with new functionality for multiple files uploads
82 | and better support for working with uploaded files. This may cause breaking
83 | changes. Please see the new api in our
84 | documentation
85 |
86 | Notable Changes
87 |
88 | - 🎈 st.balloon
has gotten a facelift with nicer balloons and smoother animations.
89 | - 🚨 Breaking Change: Following the deprecation of st.deck_gl_chart
in
90 | January 2020, we have now removed the API completely. Please use
91 | st.pydeck_chart
instead.
92 | - 🚨 Breaking Change: Following the deprecation of width
and height
for
93 | st.altair_chart
, st.graphviz_chart
, st.plotly_chart
, and
94 | st.vega_lite_chart
in January 2020, we have now removed the args completely.
95 | Please set the width and height in the respective charting library.
96 |
97 | ## Version 0.67.0
98 |
99 | Release date: September 16, 2020
100 |
101 | Highlights:
102 |
103 | - 🦷 Streamlit Components can now return bytes to your Streamlit App. To create a
104 | component that returns bytes, make sure to upgrade to the latest
105 | streamlit-component-lib.
106 |
107 | Notable Changes
108 |
109 | - 📈 Deprecation warning: Beginning December 1st, 2020 st.pyplot()
will require a figure to
110 | be provided. To disable the deprecation warning, please set deprecation.showPyplotGlobalUse
111 | to False
112 | - 🎚 st.multiselect
and st.select
are now lightning fast when working with large datasets. Thanks masa3141!
113 |
114 | ## Version 0.66.0
115 |
116 | Release date: September 1, 2020
117 |
118 | Highlights:
119 |
120 | - ✏️ st.write
is now available for use in the sidebar!
121 | - 🎚 A slider for distinct or non-numerical values is now available with st.select_slider
.
122 | - ⌗ Streamlit Components can now return dataframes to your Streamlit App. Check out our SelectableDataTable example.
123 | - 📦 The Streamlit Components library used in our Streamlit Component template is
124 | now available as a npm package (streamlit-component-lib) to simplify future upgrades to the latest version.
125 | Existing components do not need to migrate.
126 |
127 | Notable Changes
128 |
129 | - 🐼 Support StringDtype from pandas version 1.0.0
130 | - 🧦 Support for running Streamlit on Unix sockets
131 |
132 | ## Version 0.65.0
133 |
134 | Release date: August 12, 2020
135 |
136 | Highlights:
137 |
138 | - ⚙️ Ability to set page title, favicon, sidebar state, and wide mode via st.beta_set_page_config(). See our documentation for details.
139 | - 📝 Add stateful behaviors through the use of query parameters with st.experimental_set_query_params and st.experimental_get_query_params. Thanks @zhaoooyue!
140 | - 🐼 Improved pandas dataframe support for st.radio, st.selectbox, and st.multiselect.
141 | - 🛑 Break out of your Streamlit app with st.stop.
142 | - 🖼 Inline SVG support for st.image.
143 |
144 | Callouts:
145 |
146 | - 🚨Deprecation Warning: The st.image parameter format has been renamed to output_format.
147 |
148 | ## Version 0.64.0
149 |
150 | Release date: July 23, 2020
151 |
152 | Highlights:
153 |
154 | - 📊 Default matplotlib to display charts with a tight layout. To disable this,
155 | set bbox_inches
to None
, inches as a string, or a Bbox
156 | - 🗃 Deprecation warning for automatic encoding on st.file_uploader
157 | - 🙈 If gatherUserStats
is False
, do not even load the Segment library.
158 | Thanks @tanmaylaud!
159 |
160 | ## Version 0.63.0
161 |
162 | Release date: July 13, 2020
163 |
164 | Highlights:
165 |
166 | - 🧩 Support for Streamlit Components!!! See
167 | documentation for more info.
168 | - 🕗 Support for datetimes in
169 | st.slider
. And, of course, just
170 | like any other value you use in st.slider
, you can also pass in two-element lists to get a
171 | datetime range slider.
172 |
173 | ## Version 0.62.0
174 |
175 | Release date: June 21, 2020
176 |
177 | Highlights:
178 |
179 | - 📨 Ability to turn websocket compression on/off via the config option
180 | server.enableWebsocketCompression
. This is useful if your server strips HTTP headers and you do
181 | not have access to change that behavior.
182 | - 🗝️ Out-of-the-box support for CSRF protection using the
183 | Cookie-to-header token
184 | technique. This means that if you're serving your Streamlit app from multiple replicas you'll need
185 | to configure them to to use the same cookie secret with the server.cookieSecret
config option.
186 | To turn XSRF protection off, set server.enableXsrfProtection=false
.
187 |
188 | Notable bug fixes:
189 |
190 | - 🖼️ Added a grace period to the image cache expiration logic in order to fix multiple related bugs
191 | where images sent with st.image
or st.pyplot
were sometimes missing.
192 |
193 | ## Version 0.61.0
194 |
195 | Release date: June 2, 2020
196 |
197 | Highlights:
198 |
199 | - 📅 Support for date ranges in st.date_picker
. See
200 | docs
201 | for more info, but the TLDR is: just pass a list/tuple as the default date and it will be
202 | interpreted as a range.
203 | - 🗣️ You can now choose whether st.echo
prints the code above or below the output of the echoed
204 | block. To learn more, refer to the code_location
argument in the
205 | docs.
206 | - 📦 Improved @st.cache
support for Keras models and Tensorflow saved_models
.
207 |
208 | ## Version 0.60.0
209 |
210 | Release date: May 18, 2020
211 |
212 | Highlights:
213 |
214 | - st.text_area
with the height
argument
215 | (expressed in pixels). See
216 | docs for more.
217 | - 🔡 Ability to set the maximimum number of characters allowed in st.text_area
218 | or st.text_input
. Check out the max_chars
argument in the
219 | docs.
220 | - 🗺️ Better DeckGL support for the H3 geospatial indexing
221 | system. So now you can use things like H3HexagonLayer
in
222 | st.pydeck_chart
.
223 | - 📦 Improved @st.cache
support for PyTorch TensorBase and Model.
224 |
225 | ## Version 0.59.0
226 |
227 | Release date: May 05, 2020
228 |
229 | Highlights:
230 |
231 | - 🎨 New color-picker widget! Use it with
232 | st.beta_color_picker()
233 | - 🧪 Introducing st.beta_*
and st.experimental_*
function prefixes, for faster
234 | Streamlit feature releases. See
235 | docs for more info.
236 | - 📦 Improved @st.cache
support for SQL Alchemy objects, CompiledFFI, PyTorch
237 | Tensors, and builtins.mappingproxy
.
238 |
239 | ## Version 0.58.0
240 |
241 | Release date: April 22, 2020
242 |
243 | Highlights:
244 |
245 | - 💼 Made st.selectbox
filtering case-insensitive.
246 | - ㈬ Better support for Tensorflow sessions in @st.cache
.
247 | - 📊 Changed behavior of st.pyplot
to auto-clear the figure only when using
248 | the global Matplotlib figure (i.e. only when calling st.pyplot()
rather
249 | than st.pyplot(fig)
).
250 |
251 | ## Version 0.57.0
252 |
253 | Release date: March 26, 2020
254 |
255 | Highlights:
256 |
257 | - ⏲️ Ability to set expiration options for @st.cache
'ed functions by setting
258 | the max_entries
and ttl
arguments. See
259 | docs.
260 | - 🆙 Improved the machinery behind st.file_uploader
, so it's much more
261 | performant now! Also increased the default upload limit to 200MB
262 | (configurable via server.max_upload_size
).
263 | - 🔒 The server.address
config option now binds the server to that address
264 | for added security.
265 | - 📄 Even more details added to error messages for @st.cache
for easier
266 | debugging.
267 |
268 | ## Version 0.56.0
269 |
270 | Release date: February 15, 2020
271 |
272 | Highlights:
273 |
274 | - 📄 Improved error messages for st.cache. The errors now also point to the new
275 | caching docs we just released. Read more
276 | here!
277 |
278 | Breaking changes:
279 |
280 | - 🐍 As announced last month,
281 | Streamlit no longer supports Python 2. To use Streamlit you'll need
282 | Python 3.5 or above.
283 |
284 | ## Version 0.55.0
285 |
286 | Release date: February 4, 2020
287 |
288 | Highlights:
289 |
290 | - 📺 Ability to record screencasts directly from Streamlit! This allows
291 | you to easily record and share explanations about your models, analyses,
292 | data, etc. Just click ☰ then "Record a screencast". Give it a try!
293 |
294 | ## Version 0.54.0
295 |
296 | Release date: January 29, 2020
297 |
298 | Highlights:
299 |
300 | - ⌨️ Support for password fields! Just pass type="password"
to
301 | st.text_input()
.
302 |
303 | Notable fixes:
304 |
305 | - ✳️ Numerous st.cache improvements, including better support for complex objects.
306 | - 🗣️ Fixed cross-talk in sidebar between multiple users.
307 |
308 | Breaking changes:
309 |
310 | - If you're using the SessionState hack Gist, you should re-download it!
311 | Depending on which hack you're using, here are some links to save you some
312 | time:
313 | - SessionState.py
314 | - st_state_patch.py
315 |
316 | ## Version 0.53.0
317 |
318 | Release date: January 14, 2020
319 |
320 | Highlights:
321 |
322 | - 🗺️ Support for all DeckGL features! Just use
323 | Pydeck instead of
324 | st.deck_gl_chart
.
325 | To do that, simply pass a PyDeck object to
326 | st.pydeck_chart
,
327 | st.write
,
328 | or magic.
329 |
330 | Note that as a preview release things may change in the near future.
331 | Looking forward to hearing input from the community before we stabilize the
332 | API!
333 |
334 | The goals is for this to replace st.deck_gl_chart
, since it
335 | is does everything the old API did and much more!
336 |
337 | - 🆕 Better handling of Streamlit upgrades while developing. We now auto-reload
338 | the browser tab if the app it is displaying uses a newer version of Streamlit
339 | than the one the tab is running.
340 |
341 | - 👑 New favicon, with our new logo!
342 |
343 | Notable fixes:
344 |
345 | - Magic now works correctly in Python 3.8. It no longer causes
346 | docstrings to render in your app.
347 |
348 | Breaking changes:
349 |
350 | - Updated how we calculate the default width and height of all chart types.
351 | We now leave chart sizing up to your charting library itself, so please refer
352 | to the library's documentation.
353 |
354 | As a result, the width
and height
arguments have been deprecated
355 | from most chart commands, and use_container_width
has been introduced
356 | everywhere to allow you to make charts fill as much horizontal space as
357 | possible (this used to be the default).
358 |
1 | ---
2 | title: 2021 release notes
3 | slug: /develop/quick-reference/release-notes/2021
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2021 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2021. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 1.3.0
13 |
14 | Release date: Dec 16, 2021
15 |
16 | Notable Changes
17 |
18 | - 💯 Support for NumPy values in st.metric
.
19 | - 🌐 Support for Mesh Layers in PyDeck.
20 | - 📊 Updated Plotly chart version to support the latest features.
21 | - 🏀 st.spinner
element has visual animated spinner.
22 | - 🍰 st.caption
supports HTML in text with unsafe_allow_html
parameter.
23 |
24 | Other Changes
25 |
26 | - 🪲 Bug fix: Allow st.session_state
to be used to set number_input values with no warning (#4047).
27 | - 🪲 Bug fix: Fix footer alignment in wide mode (#4035).
28 | - 🐞 Bug fix: Better support for Graphviz and Bokeh charts in containers (columns, expanders, etc.) (#4039).
29 | - 🐞 Bug fix: Support inline data values in Vega-Lite (#4070).
30 | - ✍️ Types: Updated type annotations for experimental memo and singleton decorators.
31 | - ✍️ Types: Improved type annotations for st.selectbox
, st.select_slider
, st.radio
, st.number_input
, and st.multiselect
.
32 |
33 | ## Version 1.2.0
34 |
35 | Release date: Nov 11, 2021
36 |
37 | Notable Changes
38 |
39 | - ✏️ st.text_input
and st.text_area
now have a placeholder
parameter to display text when the field is empty.
40 | - 📏 Viewers can now resize the input box in st.text_area
.
41 | - 📁 Streamlit can auto-reload when files in sub-directories change.
42 | - 🌈 We've upgraded Bokeh support to 2.4.1! We recommend updating your Bokeh library to 2.4.1 to maintain functionality. Going forward, we'll let you know if there's a mismatch in your Bokeh version via an error prompt.
43 | - 🔒 Developers can access secrets via attribute notation (e.g. st.secrets.key
vs st.secrets["key"]
) just like session state.
44 | - ✍️ Publish type annotations according to PEP 561. Users now get type annotations for Streamlit when running mypy (#4025).
45 |
46 | Other Changes
47 |
48 | - 👀 Visual fixes (#3863, #3995, #3926, #3975).
49 | - 🍔 Fixes to the hamburger menu (#3968).
50 | - 🖨️ Ability to print session state (#3970).
51 |
52 | ## Version 1.1.0
53 |
54 | Release date: Oct 21, 2021
55 |
56 | Highlights
57 |
58 | - 🧠 Memory improvements: Streamlit apps allocate way less memory over time now.
59 |
60 | Notable Changes
61 |
62 | - ♻️ Apps automatically rerun now when the content of secrets.toml
changes (before this you had to refresh the page manually).
63 |
64 | Other Changes
65 |
66 | - 🔗 Redirected some links to our brand-new docs site, e.g. in exceptions.
67 | - 🪲 Bug fix: Allow initialization of range slider with session state (#3586).
68 | - 🐞 Bug fix: Refresh chart when using add_rows
with datetime
index (#3653).
69 | - ✍️ Added some more type annotation in our codebase (#3908).
70 |
71 | ## Version 1.0.0
72 |
73 | Release date: Oct 5, 2021
74 |
75 | Highlights
76 |
77 | - 🎈Announcing Streamlit 1.0! To read more about check out our 1.0 blog post.
78 |
79 | Other Changes
80 |
81 | - 🐞 Fixed an issue where using df.dtypes
to show datatypes for a DF fails while using Arrow (#3709), Image captions stay within image width and are readable (#3530).
82 |
83 | ## Version 0.89.0
84 |
85 | Release date: Sep 22, 2021
86 |
87 | Highlights
88 |
89 | - 💰 Introducing st.experimental_memo
and experimental_singleton
, a new primitive for caching! See our blog post.
90 | - 🍔 Streamlit allows developers to configure their hamburger menu to be more user-centric.
91 |
92 | Notable Changes
93 |
94 | - 💅 We updated our UI to a more polished look with a new font.
95 | - 🎨 We now support theme.base
in the theme object when it's sent to custom components.
96 | - 🧠 We've modified session state to reset widgets if any of their arguments changed even if they provide a key.
97 | - Some widget behavior may have changed, but we believe this change makes the most sense. We have added a section to our documentation describing how they behave.
98 |
99 | Other Changes
100 |
101 | - 🐞 Bug fixes: Support svgs from a URL (#3809) and that do not start with <svg>
tag (#3789).
102 |
103 | ## Version 0.88.0
104 |
105 | Release date: Sep 2, 2021
106 |
107 | Highlights
108 |
109 | - ⬇️ Introducing st.download_button
, a new button widget for easily downloading files.
110 |
111 | Notable Changes
112 |
113 | - 🛑 We made changes to improve the redacted exception experience on Streamlit Community Cloud. When client.showErrorDetails=true
exceptions display the Error Type and the Traceback, but redact the actual error text to prevent data leaks.
114 |
115 | ## Version 0.87.0
116 |
117 | Release date: Aug 19, 2021
118 |
119 | Highlights
120 |
121 | - 🔢 Introducing st.metric
, an API for displaying KPIs. Check out the demo app showcasing the functionality.
122 |
123 | Other Changes
124 |
125 | - 🐞 Bug Fixes: File uploader retains state upon expander closing (#3557), setIn Error with st.empty
(#3659), Missing IFrame embeds in docs (#3706), Fix error writing certain PNG files (#3597).
126 |
127 | ## Version 0.86.0
128 |
129 | Release date: Aug 5, 2021
130 |
131 | Highlights
132 |
133 | - 🎓 Our layout primitives are graduating from beta! You can now use st.columns
, st.container
and st.expander
without the beta_
prefix.
134 |
135 | Notable Changes
136 |
137 | - 📱 When using st.columns
, columns will stack vertically when viewport size <640px so that column layout on smaller viewports is consistent and cleaner. (#3594).
138 |
139 | Other Changes
140 |
141 | - 🐞 Bug fixes: Fixed st.date_input
crashes if its empty (#3194), Opening files with utf-8(#3022), st.select_slider
resets its state upon interaction (#3600).
142 |
143 | ## Version 0.85.0
144 |
145 | Release date: Jul 22, 2021
146 |
147 | Highlights
148 |
149 | - 🏹 Streamlit now uses Apache Arrow for serializing data frames when they are sent from Streamlit server to the front end. See our blog post.
150 | - (Users who wish to continue using the legacy data frame serialization can do so by setting the dataFrameSerialization
config option to "legacy"
in their config.toml
).
151 |
152 | Other Changes
153 |
154 | - 🐞 Bug fixes: Unresponsive pydeck example (#3395), JSON parse error message (#2324), Tooltips rendering (#3300), Colorpicker not working on Streamlit Sharing (#2689).
155 |
156 | ## Version 0.84.0
157 |
158 | Release date: Jul 1, 2021
159 |
160 | Highlights
161 |
162 | - 🧠 Introducing st.session_state
and widget callbacks to allow you to add statefulness to your apps. Check out the blog post
163 |
164 | Notable Changes
165 |
166 | - 🪄 st.text_input
now has an autocomplete
parameter to allow password managers to be used
167 |
168 | Other Changes
169 |
170 | - Using st.set_page_config to assign the page title no longer appends "Streamlit" to that title (#3467)
171 | - NumberInput: disable plus/minus buttons when the widget is already at its max (or min) value (#3493)
172 |
173 | ## Version 0.83.0
174 |
175 | Release date: Jun 17, 2021
176 |
177 | Highlights
178 |
179 | - 🛣️ Updates to Streamlit docs to include step-by-step guides which demonstrate how to connect Streamlit apps to various databases & APIs
180 |
181 | Notable Changes
182 |
183 | - 📄 st.form
now has a clear_on_submit
parameter which "resets" all the form's widgets when the form is submitted.
184 |
185 | Other Changes
186 |
187 | - Fixed bugs regarding file encodings (#3320, #3108, #2731)
188 |
189 | ## Version 0.82.0
190 |
191 | Release date: May 13, 2021
192 |
193 | Notable Changes
194 |
195 | - ♻️ Improvements to memory management by forcing garbage collection between script runs.
196 |
197 | ## Version 0.81.1
198 |
199 | Release date: Apr 29, 2021
200 |
201 | Highlights
202 |
203 | - 📝 Introducing st.form
and st.form_submit_button
to allow you to batch input widgets. Check out our blog post
204 | - 🔤 Introducing st.caption
so you can add explainer text anywhere in you apps.
205 | - 🎨 Updates to Theming, including ability to build a theme that inherits from any of our default themes.
206 | - 🚀 Improvements to deployment experience to Streamlit sharing from the app menu.
207 |
208 | Other changes
209 |
210 | - Support for binary files in Custom Components (#3144)
211 |
212 | ## Version 0.80.0
213 |
214 | Release date: Apr 8, 2021
215 |
216 | Highlights
217 |
218 | - 🔐 Streamlit now support Secrets management for apps deployed to Streamlit Sharing!
219 | - ⚓️ Titles and headers now come with automatically generated anchor links. Just hover over any title and click the 🔗 to get the link!
220 |
221 | Other changes
222 |
223 | - Added allow-downloads
capability to custom components (#3040)
224 | - Fixed markdown tables in dark theme (#3020)
225 | - Improved color picker widget in the Custom Theme dialog (#2970)
226 |
227 | ## Version 0.79.0
228 |
229 | Release date: Mar 18, 2021
230 |
231 | Highlights
232 |
233 | - 🌈 Introducing support for custom themes. Check out our blog post
234 | - 🌚 This release also introduces dark mode!
235 | - 🛠️ Support for tooltips on all input widgets
236 |
237 | Other changes
238 |
239 | - Fixed bugs regarding file encodings (#1936, #2606) and caching functions (#2728)
240 |
241 | ## Version 0.78.0
242 |
243 | Release date: Mar 4, 2021
244 |
245 | Features
246 |
247 | - If you're in the Streamlit for Teams beta, we made a few updates to how secrets work. Check the beta docs for more info!
248 | - Dataframes now displays timezones for all DateTime and Time columns, and shows the time with the timezone applied, rather than in UTC
249 |
250 | Notable Bug Fixes
251 |
252 | - Various improvement to column alignment in st.beta_columns
253 | - Removed the long-deprecated format
param from st.image
, and replaced with output_format
.
254 |
255 | ## Version 0.77.0
256 |
257 | Release date: Feb 23, 2021
258 |
259 | Features
260 |
261 | - Added a new config option client.showErrorDetails
allowing the developer to control the granularity of error messages. This is useful for when you deploy an app, and want to conceal from your users potentially-sensitive information contained in tracebacks.
262 |
263 | Notable bug fixes
264 |
265 | - Fixed bug where st.image
wasn't rendering certain kinds of SVGs correctly.
266 | - Fixed regression where the current value of an st.slider
was only shown on hover.
267 |
268 | ## Version 0.76.0
269 |
270 | Release date: February 4, 2021
271 |
272 | Notable Changes
273 |
274 | - 🎨 st.color_picker
is now out of beta. This means the old beta_color_picker function, which was marked as deprecated for the past 3 months, has now been replaced with color_picker.
275 | - 🐍 Display a warning when a Streamlit script is run directly as python script.py
.
276 | - st.image
's use_column_width
now defaults to an auto
option which will resize the image to the column width if the image exceeds the column width.
277 | - ✂️ Fixed bugs (2437 and 2247) with content getting cut off within a st.beta_expander
278 | - 📜 Fixed a bug in st.dataframe
where the scrollbar overlapped with the contents in the last column.
279 | - 💾 Fixed a bug for st.file_uploader
where file data returned was not the most recently uploaded file.
280 | - ➕ Fixed bugs (2086 and 2556) where some LaTeX commands were not rendering correctly.
281 |
282 | ## Version 0.75.0
283 |
284 | Release date: January 21, 2021
285 |
286 | Notable Changes
287 |
288 | - 🕳 st.empty
289 | previously would clear the component at the end of the script. It has now been
290 | updated to clear the component instantly.
291 | - 🛹 Previously in wide mode, we had thin margins around the webpage. This has
292 | now been increased to provide a better visual experience.
293 |
294 | ## Version 0.74.0
295 |
296 | Release date: January 6, 2021
297 |
298 | Notable Changes
299 |
300 | - 💾 st.file_uploader
. has been stabilized and the deprecation warning
301 | and associated configuration option (deprecation.showfileUploaderEncoding
) has been removed.
302 | - 📊 st.bokeh_chart
is no longer duplicated when the page loads.
303 | - 🎈 Fixed page icon to support emojis with variants (i.e. 🤦♀️ vs 🤦🏼♀️) or dashes (i.e 🌙 - crescent-moon).
304 |
1 | ---
2 | title: 2022 release notes
3 | slug: /develop/quick-reference/release-notes/2022
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2022 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2022. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 1.16.0
13 |
14 | Release date: December 14, 2022
15 |
16 | Highlights
17 |
18 | - 👩🎨 Introducing a new Streamlit theme for Altair, Plotly, and Vega-Lite charts! Check out our blog post for more information.
19 | - 🎨 Streamlit now supports colored text in all commands that accept Markdown, including st.markdown
, st.header
, and more. Learn more in our documentation.
20 |
21 | Notable Changes
22 |
23 | - 🔁 Functions cached with st.experimental_memo
or st.experimental_singleton
can contain Streamlit media elements and forms.
24 | - ⛄ All Streamlit commands that accept pandas DataFrames as input also support Snowpark and PySpark DataFrames.
25 | - 🏷 st.checkbox and st.metric can customize how to hide their labels with the label_visibility
parameter.
26 |
27 | Other Changes
28 |
29 | - 🗺️ st.map
improvements: support for upper case columns and better exception messages (#5679, #5792).
30 | - 🐞 Bug fix: st.plotly_chart
respects the figure's height attribute and the use_container_width
parameter (#5779).
31 | - 🪲 Bug fix: all commands with the icon
parameter such as st.error, st.warning, etc, can contain emojis with variant selectors (#5583).
32 | - 🐝 Bug fix: prevent st.camera_input
from jittering when resizing the browser window (#5661).
33 | - 🐜 Bug fix: update exception layout to avoid overflow of stack traces (#5700).
34 |
35 | ## Version 1.15.0
36 |
37 | Release date: November 17, 2022
38 |
39 | Notable Changes
40 |
41 | - 💅 Widget labels can contain inline Markdown. See our docs and demo app for more info.
42 | - 🎵 st.audio
now supports playing audio data passed in as NumPy arrays with the keyword-only sample_rate
parameter.
43 | - 🔁 Functions cached with st.experimental_memo
or st.experimental_singleton
can contain Streamlit widgets using the experimental_allow_widgets
parameter. This allows caching checkboxes, sliders, radio buttons, and more!
44 |
45 | Other Changes
46 |
47 | - 👩🎨 Design tweak to prevent jittering in sliders (#5612).
48 | - 🐛 Bug fix: links in headers are red, not blue (#5609).
49 | - 🐞 Bug fix: properly resize Plotly charts when exiting fullscreen (#5645).
50 | - 🐝: Bug fix: don't accidentally trigger st.balloons
and st.snow
(#5401).
51 |
52 | ## Version 1.14.0
53 |
54 | Release date: October 27, 2022
55 |
56 | Highlights
57 |
58 | - 🎨 st.button
and st.form_submit_button
support designating buttons as "primary" (for additional emphasis) or "secondary" (for normal buttons) with the type
keyword-only parameter.
59 |
60 | Notable Changes
61 |
62 | - 🤏 st.multiselect
has a keyword-only max_selections
parameter to limit the number of options that can be selected at a time.
63 | - 📄 st.form_submit_button
now has the disabled
parameter that removes interactivity.
64 |
65 | Other Changes
66 |
67 | - 🏓 st.dataframe
and st.table
accept categorical intervals as input (#5395).
68 | - ⚡ Performance improvements to Plotly charts (#5542).
69 | - 🪲 Bug fix: st.download_button
supports non-latin1 characters in filenames (#5465).
70 | - 🐞 Bug fix: Allow st.image
to render a local GIF as a GIF, not as a static PNG (#5438).
71 | - 📱 Design tweaks to the sidebar in multipage apps (#5538, #5445, #5559).
72 | - 📊 Improvements to the axis configuration for built-in charts (#5412).
73 | - 🔧 Memo and singleton improvements: support text values for show_spinner
, use datetime.timedelta
objects as ttl
parameter value, properly hash PIL images and Enum
classes, show better error messages when returning unevaluated dataframes (#5447, #5413, #5504, #5426, #5515).
74 | - 🔍 Zoom buttons in maps created with st.map
and st.pydeck_chart
use light or dark style based on the app's theme (#5479).
75 | - 🗜 Websocket headers from the current session's incoming WebSocket request can be obtained from a new "internal" (i.e.: subject to change without deprecation) API (#5457).
76 | - 📝 Improve the text that gets printed when you first install and use Streamlit (#5473).
77 |
78 | ## Version 1.13.0
79 |
80 | Release date: September 22, 2022
81 |
82 | Notable Changes
83 |
84 | - 🏷 Widgets can customize how to hide their labels with the label_visibility
parameter.
85 | - 🔍 st.map
adds zoom buttons to the map by default.
86 | - st.dataframe
supports the use_container_width
parameter to stretch across the full container width.
87 | - 🪄 Improvements to st.dataframe
sizing: Column width calculation respects column headers, supports double click between column headers to autosize, better fullscreen support, and fixes the issue with the width
parameter.
88 |
89 | Other Changes
90 |
91 | - ⌨️ st.time_input
allows for keyboard-only input (#5194).
92 | - 💿 st.memo
will warn the user when using ttl
and persist
keyword argument together (#5032).
93 | - 🔢 st.number_input
returns consistent type after rerun (#5359).
94 | - 🚒 st.sidebar
UI fixes including a fix for scrollbars in Firefox browsers (#5157, #5324).
95 | - 👩💻 Improvements to usage metrics to guide API development.
96 | - ✍️ More type hints! (#5191, #5192, #5242, #5243, #5244, #5245, #5246) Thanks harahu!
97 |
98 | ## Version 1.12.0
99 |
100 | Release date: August 11, 2022
101 |
102 | Highlights
103 |
104 | - 📊 Built-in charts (e.g. st.line_chart
) get a brand-new look and parameters x
and y
! Check out our blog post for more information.
105 |
106 | Notable Changes
107 |
108 | - ⏯ Functions cached with st.experimental_memo
or st.experimental_singleton
can now contain static st
commands. This allows caching text, charts, dataframes, and more!
109 | - st.info
, st.success
, st.error
, and st.warning
got a redesign and have a new keyword-only parameter: icon
.
111 |
112 | Other Changes
113 |
114 | - 🎚️ st.select_slider
correctly handles all floats now (#4973, #4978).
115 | - 🔢 st.multi_select
can take values from enums (#4987).
116 | - 🍊 st.slider
range values can now be set through st.session_state
(#5007).
117 | - 🎨 st.progress
got a redesign (#5011, #5086).
118 | - 🔘 st.radio
better deals with list-like dataframes (#5021).
119 | - 🧞♂️ st.cache
properly handles JSON files now (#5023).
120 | - ⚓️ Headers render markdown now when the anchor
parameter is set (#5038).
121 | - 🗻 st.image
can now load SVGs from Inkscape (#5040).
122 | - 🗺️ st.map
and st.pydeck_chart
use light or dark style based on the app's theme (#5074, #5108).
123 | - 🎈 Clicks on elements below st.balloons
and st.snow
don't get blocked anymore (#5098).
124 | - 🔝 Embedded apps have lower top padding (#5111).
125 | - 💅 Adjusted padding and alignment for widgets, charts, and dataframes (#4995, #5061, #5081).
126 | - ✍️ More type hints! (#4926, #4932, #4933)
127 |
128 | ## Version 1.11.0
129 |
130 | Release date: July 14, 2022
131 |
132 | Highlights
133 |
134 | - 🗂 Introducing st.tabs
to have tab containers in your app. See our documentation on how to use this feature.
135 |
136 | Notable Changes
137 |
138 | - ℹ️ st.metric
supports tooltips with the help
keyword parameter.
139 | - 🚇 st.columns
supports setting the gap size between columns with the gap
keyword parameter.
140 |
141 | Other Changes
142 |
143 | - 💅 Design tweaks to st.selectbox
, st.expander
, st.spinner
(#4801).
144 | - 📱 The sidebar will close when users select a page from the navigation menu on mobile devices (#4851).
145 | - 🧠 st.memo
supports dataclasses! (#4850)
146 | - 🏎 Bug fix for a race condition that destroyed widget state with rapid interaction (#4882).
147 | - 🏓 st.table
presents overflowing content to be scrollable when placed inside columns and expanders (#4934).
148 | - 🐍 Types: More updated type annotations across Streamlit! (#4808, #4809, #4856)
149 |
150 | ## Version 1.10.0
151 |
152 | Release date: June 2, 2022
153 |
154 | Highlights
155 |
156 | - 📖 Introducing native support for multipage apps! Check out our blog post and try out our new streamlit hello
.
157 |
158 | Notable Changes
159 |
160 | - ✨ st.dataframe
has been redesigned.
161 | - 🔘 st.radio
has a horizontal
keyword-only parameter to display options horizontally.
162 | - st.experimental_user
.
164 |
165 | Other Changes
166 |
167 | - 📊 Upgraded Vega-Lite library to support even more interactive charting improvements. See their release notes to find out more. (#4751).
168 | - 📈 st.vega_lite_chart
will respond to updates, particularly in response to input widgets (#4736).
169 | - 💬 st.markdown
with long text will always wrap (#4696).
170 | - 📦 Support for PDM (#4724).
171 | - ✍️ Types: Updated type annotations across Streamlit! (#4679, #4680, #4681, #4682, #4683, #4684, #4685, #4686, #4687, #4688, #4690, #4703, #4704, #4705, #4706, #4707, #4708, #4710, #4723, #4733).
172 |
173 | ## Version 1.9.0
174 |
175 | Release date: May 4, 2022
176 |
177 | Notable Changes
178 |
179 | - 🪗 st.json
now supports a keyword-only argument, expanded
on whether the JSON should be expanded by default (defaults to True
).
180 | - 🏃♀️ More performance improvements from reducing redundant work each script run.
181 |
182 | Other Changes
183 |
184 | - 🏇 Widgets when disabled
is set/unset will maintain its value (#4527).
185 | - 🧪 Experimental feature to increase the speed of reruns using configuration runner.fastReruns
. See #4628 for the known issues in enabling this feature.
186 | - 🗺️ DataFrame timestamps support UTC offset (in addition to time zone notation) (#4669).
187 |
188 | ## Version 1.8.0
189 |
190 | Release date: March 24, 2022
191 |
192 | Notable Changes
193 |
194 | - 🏃♀️ Dataframes should see performance improvements (#4463).
195 |
196 | Other Changes
197 |
198 | - 🕰 st.slider
handles timezones better by removing timezone conversions on the backend (#4348).
199 | - 👩🎨 Design improvements to our header (#4496).
200 |
201 | ## Version 1.7.0
202 |
203 | Release date: March 3, 2022
204 |
205 | Highlights
206 |
207 | - Introducing st.snow
, celebrating our acquisition by Snowflake! See more information in our blog post.
208 |
209 | ## Version 1.6.0
210 |
211 | Release date: Feb 24, 2022
212 |
213 | Other Changes
214 |
215 | - 🗜 WebSocket compression is now disabled by default, which will improve CPU and latency performance for large dataframes. You can use the server.enableWebsocketCompression
configuration option to re-enable it if you find the increased network traffic more impactful.
216 | - ☑️ 🔘 Radio and checkboxes improve focus on Keyboard navigation (#4308).
217 |
218 | ## Version 1.5.0
219 |
220 | Release date: Jan 27, 2022
221 |
222 | Notable Changes
223 |
224 | - 🌟 Favicon defaults to a PNG to allow for transparency (#4272).
225 | - 🚦 Select Slider Widget now has the disabled
parameter that removes interactivity (completing all of our widgets) (#4314).
226 |
227 | Other Changes
228 |
229 | - 🔤 Improvements to our markdown library to provide better support for HTML (specifically nested HTML) (#4221).
230 | - 📖 Expanders maintain their expanded state better when multiple expanders are present (#4290).
231 | - 🗳 Improved file uploader and camera input to call its on_change
handler only when necessary (#4270).
232 |
233 | ## Version 1.4.0
234 |
235 | Release date: Jan 13, 2022
236 |
237 | Highlights
238 |
239 | - 📸 Introducing st.camera_input
for uploading images straight from your camera.
240 |
241 | Notable Changes
242 |
243 | - 🚦 Widgets now have the disabled
parameter that removes interactivity.
244 | - 🚮 Clear st.experimental_memo
and st.experimental_singleton
programmatically by using the clear()
method on a cached function.
245 | - 📨 Developers can now configure the maximum size of a message to accommodate larger messages within the Streamlit application. See server.maxMessageSize
.
246 | - 🐍 We formally added support for Python 3.10.
247 |
248 | Other Changes
249 |
250 | - 😵💫 Calling str
or repr
on threading.current_thread()
does not cause a RecursionError (#4172).
251 | - 📹 Gracefully stop screencast recording when user removes permission to record (#4180).
252 | - 🌇 Better scale images by using a higher-quality image bilinear resampling algorithm (#4159).
253 |
1 | ---
2 | title: 2023 release notes
3 | slug: /develop/quick-reference/release-notes/2023
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2023 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2023. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 1.29.0
13 |
14 | Release date: November 30, 2023
15 |
16 | Highlights
17 |
18 | - 🔲 st.container
and st.form
now have a border
parameter to show or hide a border.
19 | - 🐍 Streamlit supports Python 3.12!
20 |
21 | Notable Changes
22 |
23 | - ⌛ st.dataframe
, st.data_editor
, and st.table
support datetime.timedelta
values (#7689, #4489).
24 | - 💀 Streamlit apps preload skeleton elements for a smoother appearance when initializing (#7598).
25 | - 🏃 Reduced the overhead of running AppTest
-simulated apps, especially for fast-running apps (#7691).
26 | - 🛁 String representations of AppTest
data are improved for a better testing and debugging experience (#7658).
27 | - 🔢 Apps can be configured to identify Enum
classes as the same if they have matching member names (#7408, #4909). Thanks, Asaurus1!
28 | - ❌ The "Made with Streamlit" footer no longer appears at the bottom of apps (#7583).
29 | - 🧹 Unused config options have been deprecated (#7584).
30 | - 🕳️ Query parameters can be empty (#7601, #7416).
31 | - 💅 Visual tweaks (#7592, #7630).
32 |
33 | Other Changes
34 |
35 | - 🦗 Bug fix: Convert floats to bytes instead of hashing to avoid hashing instability (#7754). Thanks, BlackHC!
36 | - 🦎 Bug fix: Corrected broken URLs and typos in error messages (#7746, #7764, #7770). Thanks, ObservedObserver!
37 | - 🐌 Bug fix: st.connection
correctly caches results when using two connections of the same type (#7730, #7709).
38 | - 🕸️ Bug fix: Using context managers with multithreading now displays content in the expected order (#7715, #7668). Thanks, eric-skydio!
39 | - 🦂 Bug fix: Added https fallback when obtaining the host machine's address (#7712, #7703). Thanks, LarsHill!
40 | - 🛡️ Bug fix: Added security patch for pyarrow
vulnerability. Custom components using pyarrow
table deserialization should require pyarrow>=14.0.1
(#7695, #7700).
41 | - 🦟 Bug fix: Improved typing for st.connection
(#7671). Thanks, thezanke!
42 | - 🪰 Bug fix: Retries of SnowflakeConnection
methods are narrowed to only occur with transient errors to avoid unnecessary repeated errors (#7645, #7637).
43 | - 🏗️ Removed the v0 testing framework which was undocumented (#7657).
44 | - 🪳 Bug fix: The navigation expander arrow no longer disappears (#7634, #7547).
45 | - ❄️ Improved the error message for SnowflakeConnection
when a configuration is not found (#7652).
46 | - 🕷️ Bug fix: st.rerun
no longer causes a RecursionError
when used with st.chat_input
(#7643, #7629).
47 | - 🐞 Bug fix: st.file_uploader
no longer causes an extra rerun and therefore doesn't conflict with st.chat_input
(#7641, #7556).
48 | - 🐝 Bug fix: AppTest
no longer raises an error when encountering st.container
(#7644, #7636).
49 | - 🪲 Bug fix: Graphviz charts scale correctly when exiting fullscreen view (#7398, #7527).
50 | - 🎥 Bug fix: "Record a screencast" is hidden when known to be unsupported in a browser (#7604).
51 | - 🐛 Bug fix: Increased the top padding of embedded apps to better display the dataframe toolbar (#7681, #7609, #7607).
52 | - 🐜 Bug fix: st.rerun
uses NoReturn
for improved type checking (#7422) Thanks, kongzii.
53 |
54 | ## Version 1.28.0
55 |
56 | Release date: October 26, 2023
57 |
58 | Release videos
59 |
60 | - Introducing AppTest
61 |
62 | Highlights
63 |
64 | - 🧪 Introducing a new testing framework for Streamlit apps! Check out our documentation to learn how to build automated tests for your apps.
65 | - 💻 Announcing the general availability of st.connection
, a command to conveniently manage connections in Streamlit apps. Check out the docs to learn more.
66 | - ❄️ SnowparkConnection
has been upgraded to the new and improved SnowflakeConnection
— the same, great functionality plus more! Check out our built-in connections.
67 | - 🛠️ st.dataframe
and st.data_editor
have a new toolbar! Users can search and download data in addition to enjoying improved UI for row additions and deletions. See our updated guide on Dataframes.
68 |
69 | Notable Changes
70 |
71 | - 🌀 When using a spinner with cached functions, the spinner will be overlaid instead of pushing content down (#7488).
72 | - 📅 st.data_editor
now supports datetime index editing (#7483).
73 | - 🔢 Improved support for decimal.Decimal
in st.dataframe
and st.data_editor
(#7475).
74 | - 🥸 Global kwargs were added for hashlib
(#7527, #7526). Thanks, DueViktor!
75 | - 📋 st.components.v1.iframe
now permits writing to clipboard (#7487). Thanks, dilipthakkar!
76 | - 📝 SafeSessionState
disconnect was replaced with script runner yield points for improved efficiency and clarity (#7373).
77 | - 🤖 The Langchain callback handler will show the full input string inside the body of a st.status
when the input string is too long to show as a label (#7478). Thanks, pokidyshev!
78 | - 📈 st.graphviz_chart
now supports using different Graphviz layout engines (#7505, #4089).
79 | - 🦋 Assorted visual tweaks (#7486, #7592).
80 | - 📊 plotly.js
was upgraded to version 2.26.1 (#7449, #7476, #7045).
81 | - 💽 Legacy serialization for DataFrames was removed. All DataFrames will be serialized by Apache Arrow (#7429).
82 | - 🖼️ Compatibility for Pillow 10.x was added (#7442).
83 | - 📬 Migrated _stcore/allowed-message-origins
endpoint to _stcore/host-config
(#7342).
84 | - 💬 Added post_parent_message
platform command to send custom messages from a Streamlit app to its parent window (#7522).
85 |
86 | Other Changes
87 |
88 | - ⌨️ Improved string dtype handling for DataFrames (#7479).
89 | - ✒️ st.write
will avoid using unsafe_allow_html=True
if possible (#7432).
90 | - 🐛 Bug fix: Implementation of st.expander
was simplified for improved behavior and consistency (#7247, #2839, #4111, #4651, #5604).
91 | - 🪲 Bug fix: Multipage links in the sidebar are now aligned with other sidebar elements (#7531).
92 | - 🐜 Bug fix: st.chat_input
won't incorrectly prompt for label
parameter in IDEs (#7560).
93 | - 🐝 Bug fix: Scroll bars correctly overlay st.dataframe
and st.data_editor
without adding empty space (#7090, #6888).
94 | - 🐞 Bug fix: st.chat_message
behaves correctly with the removal of AutoSizer (#7504, #7473).
95 | - 🕷️ Bug fix: Anchor links are reliably produced for non-English headers (#7454, #5291).
96 | - ☃️ Bug fix: st.connections.SnowparkConnection
more accurately detects when it's running within Streamlit in Snowflake (#7502).
97 | - 🪳 Bug fix: A user-friendly warning is shown when exceeding the size limitations of a pandas Styler
object (#7497, #5953).
98 | - 🪰 Bug fix: st.data_editor
automatically converts non-string column names to strings (#7485, #6950).
99 | - 🦠 Bug fix: st.data_editor
correctly identifies non-range indices as a required column (#7481, #6995).
100 | - 🦟 Bug fix: st.file_uploader
displays compound file extensions like csv.gz
correctly (#7362). Thanks, mo42!
101 | - 🦂 Bug fix: Column Configuration no longer uses deprecated type checks (#7496, #7477, #7550). Thanks, c-bik!
102 | - 🦗 Bug fix: Additional toolbar items no longer stack vertically (#7470, #7471).
103 | - 🕸️ Bug fix: Column Configuration no longer causes a type warning in Mypy (#7457). Thanks, kopp!
104 | - 🐌 Bug fix: Bokeh Sliders no longer cause JavaScript errors (#7441, #7171).
105 | - 🦎 Bug fix: Caching now recognizes DataFrames with the same values but different column names as different (#7331, #7086).
106 |
107 | ## Version 1.27.0
108 |
109 | Release date: September 21, 2023
110 |
111 | Highlights
112 |
113 | - ✨ Introducing st.scatter_chart
— a new, simple chart element to build scatter charts Streamlit-y fast and easy! See our documentation.
114 | - 🔗 Introducing st.link_button
! Want to open an external link in a new tab with a bit more pizazz than a plain-text link? Check out our documentation to see how.
115 | - 🏃 Announcing the general availability of st.rerun
, a command to interrupt your script and trigger an immediate rerun.
116 |
117 | Notable Changes
118 |
119 | - 👻 You can initialize widgets with an empty state by setting None
as an initial value for st.number_input
, st.selectbox
, st.date_input
, st.time_input
, st.radio
, st.text_input
, and st.text_area
!
120 | - 📤 st.download_button
now uses target="_self"
instead of opening a new tab (#7151, #7132).
121 | - 🧟 Removed unmaintained pympler
dependency (#7193, #7131). Thanks, rudyardrichter!
122 |
123 | Other Changes
124 |
125 | - 🐛 Bug fix: st.multiselect
now shows a correct message when no result matches a user's search (#7205, #7116).
126 | - 🪲 Bug fix: st.experimental_user
now defaults to [email protected]
(#7219, #7215).
127 | - 🐜 Bug fix: st.slider
labels don't overlap when small ranges are selected (#7221, #3385).
128 | - 🐝 Bug fix: Type-checking correctly identifies all string types to avoid hashing errors (#7255, #6455).
129 | - 🐞 Bug fix: JSON is parsed with JSON5 to avoid errors from null values when using st.pydeck_chart
(#7256, #5799).
130 | - 🕷️ Bug fix: Identical widgets on different pages are correctly interpreted by Streamlit as distinct (#7264, #6146).
131 | - 🦋 Bug fix: Visual tweaks to widgets for responsive behavior (#7145).
132 | - 🪳 Bug fix: SVGs are accurately displayed (#7183, #3882).
133 | - 🪰 Bug fix: st.video
correctly updates with changes to start_time
(#7257, #7126).
134 | - 🦠 Bug fix: Additional error handling was added to st.session_state
(#7280, #7206).
135 | - 🦟 Bug fix: st.map
correctly refreshes with new data (#7307, #7294).
136 | - 🦂 Bug fix: The decorative app header line is no longer covered by the sidebar (#7297, #6264).
137 | - 🦗 Bug fix: st.code
no longer triggers a CachedStFunctionWarning
(#7306, #7055).
138 | - 🕸️ Bug fix: st.download_button
no longer resets with different data
(#7316, #7308).
139 | - 🐌 Bug fix: Widgets consistently recognize user interaction while a page is still running, with or without fastRerun
enabled (#7283, #6643).
140 | - 🦎 Bug fix: st.tabs
was improved to better handle and render conditionally appearing tabs (#7287, #7310, #5454, #7040).
141 |
142 | ## Version 1.26.0
143 |
144 | Release date: August 24, 2023
145 |
146 | Highlights
147 |
148 | - 🤖 Introducing st.status
to display output from long-running processes and external API calls (#7140). Works great with st.chat_message
! See our documentation for how to use this feature.
149 | - 🚥 Introducing st.toggle
— an alternative to st.checkbox
when you need an on/off switch.
150 |
151 | Notable Changes
152 |
153 | - 🎨 Simple chart elements have a color
parameter to set the color of your data points or series (#7022).
154 | - 🌈 Markdown supports rainbow and gray colors (#7106, #7179).
155 | - 📏 st.header
and st.subheader
have optional, colored dividers (#7133).
156 | - 🚀 Deploying to Community Cloud is even easier—locally running apps have a deploy button in their toolbars (#7085, #6935).
157 | - 🖌️ st.download_button
has a new parameter type
for theming (#7056, #7038).
158 | - 🤖 st.chat_message
has ai and human presets for messages (#7094).
159 | - 💅 st.radio
options support markdown and have captions (#7018, #7105, #6085).
160 | - 🧼 Assorted visual tweaks (#7050, #894).
161 | - 🛏️ Replaced deprecated imghdr
dependency with pillow
(#7081, #7027).
162 | - 🔢 st.number_input
's step buttons (+/-) are ignored during tabbing navigation (#7154). Thanks @denck007!
163 |
164 | Other Changes
165 |
166 | - 🍞 Bug fix: Toast messages are no longer blocked by st.chat_input
(#7204, #7115).
167 | - 🕸️ Bug fix: Widget IDs are now stable to prevent inconsistent statefulness (#7003).
168 | - 🦟 Bug fix: Browser autofill is correctly recognized within forms now (#7150, #7101, #7084).
169 | - 🪱 Bug fix: st.file_uploader
no longer causes session state to reset when a websocket connection is dropped and reconnected (#7149, #7025).
170 | - 🏎️ Bug fix: Pydeck JSON data is cached for improved performance (#7113, #5532).
171 | - 🦋 Bug fix: st.chat_input
no longer submits prematurely while typing with an input method editor (#6993).
172 | - 🐞 Bug fix: Label backgrounds for st.tabs
are now transparent (#7070, #5707).
173 | - 🐝 Bug fix: Page width is no longer ignored when using the help
parameter in st.button
(#7033, #6161).
174 | - 🐜 Bug fix: Tweaked Altair color specification for improved visibility in dark mode (#7061, #3343).
175 | - 🪲 Bug fix: st.chat_message
can correctly use local images as avatars (#7130).
176 | - 🐛 Bug fix: Specified that MD5 is not used for security (#7122, #7120).
177 | - 🪄 Bug fix: Async function docstrings are ignored by Streamlit magic (#7143, #7137).
178 |
179 | ## Version 1.25.0
180 |
181 | Release date: July 20, 2023
182 |
183 | Highlights
184 |
185 | - 🍞 Introducing st.toast
— a command to briefly show toast messages to users in the bottom-right corner of apps. See our documentation on how to use this feature.
186 |
187 | Notable Changes
188 |
189 | - 🗺️ st.map
now has parameters for latitude
, longitude
, color
, and size
to customize data points (#6896).
190 | - 🚩 st.multiselect
supports setting placeholders and specifying the maximum number of selections via the placeholder
and max_selections
keyword-only arguments, respectively (#6901, #4750). Thanks, @fhiroki!
191 | - 📅 Customize the date format for st.date_input
with the format
parameter (#6974, #5234).
192 | - ↩️ Forms can now be submitted with Enter/Return while inside st.text_input
, st.number_input
, or st.text_area
(#6911, #3790).
193 | - 🍢 The app menu icon in the upper-right corner of apps has been changed from "☰" to "⋮" (#6947).
194 |
195 | Other Changes
196 |
197 | - ⛓️ Minimum required versions increased for multiple Python dependencies, including numpy>=1.19.3
and pandas>=1.3.0
(#6802).
198 | - 🛡️ protobufjs
was bumped from 7.2.1 to 7.2.4 (#6959).
199 | - ✨ Visual design tweaks to Streamlit's input widgets (#6944).
200 | - 🦋 Bug Fix: st.slider
now accepts general number types like numpy.int64
instead of just int
and float
(#6816, #6815). Thanks, @milliams!
201 | - 🐜 Bug Fix: Data labels for st.slider
and st.select_slider
no longer overflow when inside st.expander
(#6828, #6297).
202 | - 🐛 Bug Fix: Elements no longer re-render from scratch with each rerun (#6923, #6920).
203 | - 🐞 Bug Fix: st.data_editor
hashes styler objects correctly for stability across reruns (#6815, #6898).
204 | - 🐝 Bug Fix: Fixed the padding for embedded apps using st.chat_input
to prevent messages being cutoff (#6979).
205 |
206 | ## Version 1.24.0
207 |
208 | Release date: June 27, 2023
209 |
210 | Highlights
211 |
212 | - 💬 Introducing st.chat_message
and st.chat_input
— two new chat elements that let you build conversational apps. Learn how to use these features in your LLM-powered chat apps in our tutorial.
213 | - 💾 Streamlit's caching decorators now allow you to customize Streamlit's hashing of input parameters with the keyword-only argument hash_funcs
.
214 |
215 | Notable Changes
216 |
217 | - 🐍 We've deprecated support for Python 3.7 in the core library and Streamlit Community Cloud (#6868).
218 | - 📅 st.cache_data
and st.cache_resource
can hash timezone-aware datetime
objects (#6812, #6690, #5110).
219 |
220 | Other Changes
221 |
222 | - ✨ Visual design tweaks to Streamlit's input widgets (#6817).
223 | - 🐛 Bug fix: st.write
pretty-prints dataclasses using st.help
(#6750).
224 | - 🪲 Bug fix: st.button
's height is consistent with that of other widgets (#6738).
225 | - 🐜 Bug fix: Upgraded the react-range
frontend dependency to fix the memory usage of sliders (#6764, #5436). Thanks @wolfd!
226 | - 🐝 Bug fix: Pydantic validators no longer result in exceptions on app reruns (#6664, #3218).
227 | - 🐞 Bug fix: streamlit config show
honors newlines (#6758, #2868).
228 | - 🪰 Bug fix: Fixed a race condition to ensure Streamlit reruns the latest code when the file changes (#6884).
229 | - 🦋 Bug fix: Apps no longer rerun when users click anchor links (#6834, #6500).
230 | - 🕸️ Bug fix: Added robust out-of-bounds checks for min_value
and max_value
in st.number_input
(#6847, #6797).
231 |
232 | ## Version 1.23.0
233 |
234 | Release date: June 1, 2023
235 |
236 | Highlights
237 |
238 | - ✂️ Announcing the general availability of st.data_editor, a widget that allows you to edit DataFrames and many other data structures in a table-like UI. Breaking change: the data editor's representation used in st.session_state
was altered. Find out more about the new format in Access edited data.
239 | - ⚙️ Introducing the Column configuration API with a suite of methods to configure the display and editing behavior of st.dataframe
and st.data_editor
columns (e.g. their title, visibility, type, or format). Keep an eye out for a detailed blog post and in-depth documentation upcoming in the next two weeks.
240 | - 🔌 Learn to use st.experimental_connection
to create and manage data connections in your apps with the new Connecting to data docs and video tutorial.
241 |
242 | Notable Changes
243 |
244 | - 📊 Streamlit now supports Protobuf 4 and Altair 5 (#6215, #6618, #5626, #6622).
245 | - ☎️ st.dataframe and st.data_editor can hide index columns with hide_index
, specify the display order of columns with column_order
, and disable editing for individual columns with the disabled
parameter.
246 | - ⏱️ The ttl
parameter in st.cache_data and st.cache_resource accepts formatted strings, so you can simply say ttl="30d"
, ttl="1h30m"
and any other combination of w
, d
, h
, m
, s
supported by Pandas's Timedelta constructor (#6560).
247 | - 📂 st.file_uploader
now interprets the type
parameter more accurately. For example, "jpg" or ".jpg" now accept both "jpg" and "jpeg" extensions. This functionality has also been extended to "mpeg/mpg", "tiff/tif", "html/htm", and "mpeg4/mp4".
248 | - 🤫 The new global.disableWidgetStateDuplicationWarning
configuration option allows the silencing of warnings triggered by setting widget default values and keyed session state values concurrently (#3605, #6640). Thanks, @antonAce!
249 |
250 | Other Changes
251 |
252 | - 🏃♀️Improved startup time by lazy loading some dependencies (#6531).
253 | - 👋 Removed st.beta_*
and st.experimental_show
due to deprecation and low-use (#6558)
254 | - 🚀 Further improvements to st.dataframe and st.data_editor:
255 | - Improved editing on mobile devices for the data editor (#6548).
256 | - All editable columns have an icon in their column header and support tooltips (#6550, #6561).
257 | - Enable editing for columns containing datetime, date, or time values (#6025).
258 | - New input validation options for columns in the data editor, such as max_chars
and validate
for text columns, and min_value
, max_value
and step
for number columns (#6563).
259 | - Improved type parsing capabilities in the data editor (#6551).
260 | - Unified missing values to None
in returned data structures (#6544).
261 | - A warning is shown in cells when integers exceed the maximum safe value of (2^53) -1
(#6311, #6549).
262 | - Prevented editing the sessions state by showing a warning (#6634).
263 | - Fixed issues with list columns sometimes breaking the frontend (#6644).
264 | - Fixed a display issue with index columns using category dtype (#6680, #6598).
265 | - Fixed an issue that prevented a rerun when adding empty rows (#6598).
266 | - Unified the behavior between st.data_editor
and st.dataframe
related to auto-hiding the index column(s) based on the input data (#6659, #6598)
267 | - 🛡️ Streamlit's Security Policy can be found in its GitHub repository (#6666).
268 | - 🤏 Documented the integer size limit for st.number_input
and st.slider
(#6724).
269 | - 🐍 The majority of Streamlit's Python dependencies have set a maximum allowable version, with the standard upper limit set to the next major version, but not inclusive of it (#6691).
270 | - 💅 UI design improvements to in-app modals (#6688).
271 | - 🐞 Bug fix: st.date_input
's date selector is equally visible in dark mode (#6072, #6630).
272 | - 🐜 Bug fix: the sidebar navigation expansion indicator in multipage apps is restored (#6731).
273 | - 🐛 Bug fix: The docstring and exception message for st.set_page_config
have been updated to clarify that this command can be invoked once for each page within a multipage app, rather than once per entire app (#6594).
274 | - 🐝 Bug fix: st.json
no longer collapses multiple spaces in both keys and values with single space when rendered (#6657, #6663).
275 |
276 | ## Version 1.22.0
277 |
278 | Release date: April 27, 2023
279 |
280 | Highlights
281 |
282 | - 🔌 Introducing st.experimental_connection
: Easily connect your app to data sources and APIs using our new connection feature. Find more details in the API reference, and stay tuned for an upcoming blog post and in-depth documentation! In the meantime, explore our updated MySQL and Snowflake connection tutorials for examples of this feature.
283 |
284 | Notable Changes
285 |
286 | - 🐼 Streamlit now supports Pandas 2.0 (#6413, #6378, #6507). Thanks, connortann!
287 | - 🍔 Customize the visibility of items in the toolbar, options menu, and the settings dialog using the client.toolbarMode
config option (#6174).
288 | - 🪵 Streamlit logs now reside in the "streamlit" namespace instead of the root logger, enabling app developers to better manage log handling (#3978, #6377).
289 |
290 | Other Changes
291 |
292 | - 🔏 CLI parameters can no longer be used to set sensitive configuration values (#6376).
293 | - 🤖 Improved the debugging experience by reducing log noise (#6391).
294 | - 🐞 Bug fix: @st.cache_data
decorated functions support UUID objects as parameters (#6440, #6459).
295 | - 🐛 Bug fix: Tabbing through buttons and other elements now displays a red border only when focused, not when clicked (#6373).
296 | - 🪲 Bug fix: st.multiselect
's clear icon is larger and includes a hover effect (#6471).
297 | - 🐜 Bug fix: Custom theme font settings no longer apply to code blocks (#6484, #6535).
298 | - ©️ Bug fix: st.code
's copy-to-clipboard button appears when you hover on code blocks (#6490, #6498).
299 |
300 | ## Version 1.21.0
301 |
302 | Release date: April 6, 2023
303 |
304 | Highlights
305 |
306 | - 📏 Introducing st.divider
— a command that displays a horizontal line in your app. Learn how to use this command in its API reference.
307 | - 🔏 Streamlit now supports the use of a global secrets.toml
file, in addition to a project-level file, to easily store and securely access your secrets. Learn more in Secrets management.
308 | - 🚀 st.help has been revamped to show more information about object methods, attributes, classes, and more, which is great for debugging (#5857, #6382)!
309 |
310 | Notable Changes
311 |
312 | - 🪜 st.time_input supports adding a stepping interval with the keyword-only step
parameter (#6071).
313 | - ❓ Most text elements can include tooltips with the help
parameter (#6043).
314 | - use_container_width
parameter to set the chart to the container width (now all chart elements support this parameter) (#6067).
315 | - 👩💻 st.code supports optionally displaying line numbers to the code block's left with the boolean line_numbers
parameter (#5756, #6042).
316 | - ⚓ Anchors in header elements can be turned off by setting anchor=False
(#6158).
317 |
318 | Other Changes
319 |
320 | - 🐼 st.table and st.dataframe support pandas.Period
, and number and boolean types in categorical columns (#2547, #5429, #5329, #6248).
321 | - 🕸️ Added .webp
to the list of allowed static file extensions (#6331)
322 | - 🐞 Bug fix: stop script execution on websocket close to immediately clear session information (#6166, #6204).
323 | - 🐜 Bug fixes: updated allowed/disallowed label markdown behavior such that unsupported elements are unwrapped and only their children (text contents) render (#5872, #6036, #6054, #6163).
324 | - 🪲 Bug fixes: don't push browser history states on rerun, use HTTPS to load external resources in streamlit hello
, and make the browser back button work for multipage apps (#5292, #6266, #6232). Thanks, whitphx!
325 | - 🐝 Bug fix: avoid showing emoji on non-UTF-8 terminals. (#2284, #6088). Thanks, kcarnold!
326 | - 📁 Bug fix: override default use of File System Access API for react-dropzone
so that st.file_uploader
's File Selection Dialog only shows file types corresponding to those included in the type
parameter (#6176, #6315).
327 | - 💾 Bug fix: make the .clear()
method on cache-decorated functions work (#6310, #6321).
328 | - 🏃 Bug fix: st.experimental_get_query_params
doesn't need reruns to work (#6347, #6348). Thanks, PaleNeutron!
329 | - 🐛 Bug fix: CachedStFunctionWarning
mentions experimental_allow_widgets
instead of the deprecated suppress_st_warning
(#6216, #6217).
330 |
331 | ## Version 1.20.0
332 |
333 | Release date: March 09, 2023
334 |
335 | Notable Changes
336 |
337 | - 🔐 Added support for configuring SSL to serve apps directly over HTTPS (#5969).
338 | - 🖼️ Granular control over app embedding behavior with the /?embed
and /?embed_options
query parameters. Learn how to use this feature in our docs (#6011, #6019).
339 | - ⚡ Enabled the runner.fastReruns
configuration option by default to make apps much more responsive to user interaction (#6200).
340 |
341 | Other Changes
342 |
343 | - 🍔 Cleaned up the hamburger menu by removing the least used options (#6080).
344 | - 🖨️ Design changes to ensure apps being printed or saved as a PDF look good (#6180).
345 | - 🐞 Bug fix: improved dtypes
checking in st.experimental_data_editor
(#6185, #6188).
346 | - 🐛 Bug fix: properly position st.metric
's help
tooltip when not inside columns (#6168).
347 | - 🪲 Bug fix: regression in retrieving messages from the server's ForwardMsgCache
(#6210).
348 | - 🌀 Bug fix: st.cache_data
docstring for the show_spinner
param now lists str
as a supported type (#6207, #6213).
349 | - ⏱️ Made ping and websocket timeouts far more forgiving (#6212).
350 | - 🗺️ st.map
and st.pydeck_chart
docs state that Streamlit's Mapbox token will not work indefinitely (#6143).
351 |
352 | ## Version 1.19.0
353 |
354 | Release date: February 23, 2023
355 |
356 | Highlights
357 |
358 | - ✂️ Introducing st.experimental_data_editor
, a widget that allows you to edit DataFrames and many other data structures in a table-like UI. Read more in our documentation and blog post.
359 |
360 | Other Changes
361 |
362 | - ✨ Streamlit's GitHub README got a new look (#6016).
363 | - 🌚 Improved readability of styled dataframe cells in dark mode (#6060, #6098).
364 | - 🐛 Bug fix: make apps work again in the latest versions of Safari, and in Chrome with third-party cookies blocked (#6092, #6094, #6087, #6100).
365 | - 🐞 Bug fix: refer to new cache primitives in the "Clear cache" dialog and error messages (#6082, #6128).
366 | - 🐝 Bug fix: properly cache class member functions and instance methods (#6109, #6114).
367 | - 🐜 Bug fix: regression in st.metric
tooltip position (#6093, #6129).
368 | - 🪲 Bug fix: allow fullscreen button to show for dataframes, charts, etc, in expander (#6083, #6148).
369 |
370 | ## Version 1.18.0
371 |
372 | Release date: February 09, 2023
373 |
374 | Highlights
375 |
376 | - 🎊 Introducing @st.cache_data
and @st.cache_resource
— two new caching commands to replace st.cache
! Check out our blog post and documentation for more information.
377 |
378 | Notable Changes
379 |
380 | - 🪆 st.columns
supports up to one level of column nesting (i.e., columns inside columns) in the main area of the app.
381 | - ⏳ st.progress
supports adding a message to display above the progress bar with the text
keyword parameter.
382 | - st.button
has an optional use_container_width
parameter to allow you to stretch buttons across the full container width.
383 | - 🐍 We formally added support for Python 3.11.
384 | - 🖨️ Save your app as a PDF via the "Print" option in your app's hamburger menu.
385 | - 🛎️ Apps can serve small, static media files via the enableStaticServing
config option. See our documentation on how to use this feature and our demo app for an example.
386 |
387 | Other Changes
388 |
389 | - 🏁 All Streamlit endpoints (including /healthz
) have been renamed to have a consistent pattern and avoid any clashes with reserved endpoints of GCP (notably Cloud Run and App Engine) (#5534).
390 | - ⚡ Improved caching performance when multiple sessions access an uncomputed cached value simultaneously (#6017).
391 | - 🚧 Streamlit only displays deprecation warnings in the browser when the client.showErrorDetails
config option is set to True
. Deprecation warnings always get logged to the console, regardless of whether they're displayed in-browser (#5945).
392 | - 🏓 Refactored the st.dataframe
internals to improve dataframe handling and conversion, such as detecting more types, converting key-value dicts to dataframes, and more (#6026, #6023).
393 | - 💽 The behavior of widget labels when they are passed unsupported Markdown elements is documented (#5978).
394 | - 📊 Bug fix: Plotly improvements — upgraded multiple frontend dependencies, including Plotly, to the latest version to properly redraw cached charts, make Plotly mapbox animations work, and allow users to update the figure layout when using the Streamlit theme (#5885, #5967, #6055).
395 | - 📶 Bug fix: allow browser tabs that transiently disconnect (due to a network blip, load balancer timeout, etc.) to avoid losing all of their state (#5856).
396 | - 📱 Bug fix: the keyboard is hidden on mobile when st.selectbox
and st.multiselect
have less than 10 options (#5979).
397 | - 🐝 Bug fix: design tweaks to st.metric
, st.multiselect
, st.tabs
, and menu items to prevent label overflow and scrolling issues, especially with small viewport sizes (#5933, #6034).
398 | - 🐞 Bug fix: switched to a functioning Twemoji URL from which page favicons are loaded in st.set_page_config
(#5943).
399 | - ✍️ More type hints (#5986). Thanks, harahu!
400 |
401 | ## Version 1.17.0
402 |
403 | Release date: January 12, 2023
404 |
405 | Notable Changes
406 |
407 | - 🪄 @st.experimental_singleton
supports an optional validate
parameter that accepts a validation function for cached data and is called each time the cached value is accessed.
408 | - 💾 @st.experimental_memo
's persist
parameter can also accept booleans.
409 |
410 | Other Changes
411 |
412 | - 📟 Multipage apps exclude __init__.py
from the page selector (#5890).
413 | - 📐 The iframes of embedded apps have the ability to dynamically resize their height (#5894).
414 | - 🐞 Bug fix: thumb values of range sliders respect the container width (#5913).
415 | - 🪲 Bug fix: all examples in docstrings of Streamlit commands contain relevant imports to make them reproducible (#5877).
416 |
1 | ---
2 | title: 2024 release notes
3 | slug: /develop/quick-reference/release-notes/2024
4 | description: A changelog of highlights and fixes for each version of Streamlit.
5 | keywords: changelog, release notes, version history
6 | ---
7 |
8 | # 2024 release notes
9 |
10 | This page contains release notes for Streamlit versions released in 2024. For the latest version of Streamlit, see Release notes.
11 |
12 | ## Version 1.41.0
13 |
14 | Release date: December 10, 2024
15 |
16 | Notable Changes
17 |
18 | - 🔲 st.metric
and st.columns
have a parameter to show an optional border (#9927, #9928).
19 | - 🎨 Text and background color in Markdown can use the "primary" color from the theme.primaryColor
configuration option (#9676).
20 | - 🥶 You can freeze columns with column configuration to make them always visible when scrolling horizontally (#9535, #7078).
21 | - 3️⃣ The type
parameter for buttons accepts a new option, "tertiary"
(#9923).
22 | - 🚶♂️ Streamlit supports pathlib.Path
objects everywhere you can use a string path (#9711, #9783).
23 | - ⏱️ st.date_input
and st.time_input
accept ISO formatted strings for initial values (#9753).
24 | - 💬 st.write_stream
accepts async generators, which it converts internally to sync generators (#8724, #8161).
25 | - 🪵 The client.showErrorDetails
configuration option has additional values to show or hide more information (#9909).
26 | - 🔎 When Streamlit shows stack traces in the app for uncaught exceptions, internal code is omitted or reduced for easier debugging (#9913).
27 | - 📈 st.line_chart
shows tooltips for the nearest point on hover (#9674).
28 | - 🌐 st.html
will attempt to convert non-string objects with ._repr_html_()
before falling back to str()
(#9877).
29 | - 🐍 Streamlit supports Python 3.13 and no longer supports Python 3.8 (#9635).
30 |
31 | Other Changes
32 |
33 | - 🔣 Material Symbols have been updated with the latest icons (#9813, #9810).
34 | - 👽 Streamlit supports Watchdog version 6 (#9785). Thanks, RubenVanEldik.
35 | - 🌀 Bug fix: Streamlit only shows cached function spinners on cache misses and doesn't show spinners for nested cached functions (#9956, #9951).
36 | - 🔈 Bug fix: Streamlit's audio buffer handles channels better to correctly play audio recordings in Firefox (#9885, #9799).
37 | - 🦊 Bug fix: URL patterns are matched correctly to allow Community Cloud developer tools to display correctly in Firefox (#9849, #9848).
38 | - ☠️ Bug fix: Corrected a performance and alignment problem with containers (#9901, #9456, #9560).
39 | - 👻 Bug fix: st.rerun
will raise an error if an invalid scope
is passed to it (#9911, #9908).
40 | - 🦋 Bug fix: Dataframe toolbars show correctly in dialogs (#9897, #9461).
41 | - 🦀 Bug fix: LinkColumn
regex for display_text
uses the correct URI decoding (#9895, #9893).
42 | - 🦎 Bug fix: st.dataframe
has correct type hinting when on_selection="ignore"
(#9898, #9669).
43 | - 🐌 Bug fix: Padding is applied consistently for wide and centered layout mode (#9882, #9707).
44 | - 🕸️ Bug fix: st.graphviz_chart
is displayed correctly when use_container_width=True
(#9867, #9866).
45 | - 🦗 Bug fix: The overloaded definitions of st.pills
and st.segmented_control
use the correct selection-mode default (#9801). Thanks, RubenVanEldik!
46 | - 🦂 Bug fix: st.text_area
(and other widgets) are correctly submitted in a form when using Ctrl+Enter
(#9847, #9841).
47 | - 🦟 Bug Fix: st.write
renders DeltaGenerator
objects with st.help
(#9828, #9827).
48 | - 🦠 Bug fix: st.text_area
correctly matches the value in Session State when used with a key (#9829, #9825).
49 | - 🪰 Bug fix: st.text_input
does not trigger a rerun when a user submits an unchanged value (#9826).
50 | - 🪳 Bug fix: Improved styling for st.exception
to fix overflow and incorrect padding (#9818, #9817, #9816).
51 | - 🕷️ Bug fix: Large dataframe don't overflow and cover the dataframe toolbar in fullscreen mode (#9803, #9798).
52 | - 🐞 Bug fix: st.audio_input
shows the correct time on recording in time zones with a half-hour offset (#9791, #9631).
53 | - 🐝 Bug fix: In iOS, st.number_input
shows a number pad instead of a keyboard when in focus (#9766, #9763).
54 | - 🐜 Bug fix: Widget keys containing hyphens are correctly added to HTML classes in the DOM with an st-key-
prefix (#9793).
55 | - 🪲 Bug fix: Audio files created by st.audio_input
include a timestamp to ensure unique file names (#9768).
56 | - 🐛 Bug fix: Double slash URL pathnames do not create a 301 redirect (#9754, #9690).
57 |
58 | ## Version 1.40.0
59 |
60 | Release date: November 6, 2024
61 |
62 | Highlights
63 |
64 | - 💊 Introducing st.pills
to create a single- or multi-select group of pill-buttons.
65 | - 🎛️ Introducing st.segmented_control
to create a segmented button or button group.
66 | - 🎤 Announcing the general availability of st.audio_input
, a widget to let users record sound with their microphones.
67 |
68 | Notable Changes
69 |
70 | - ➡️ Markdown renders a limited set of typographical symbols (arrows and comparators).
71 | - <img src="/logo.svg" style={{ display: "inline-block", width: "1em" }} /> You can use :streamlit:
to render the Streamlit logo in Markdown.
72 | - 🐍 st.text
wraps text and no longer uses monospace font.
73 | - 🪣 You can set use_container_width
for st.image
. use_column_width
is deprecated.
74 | - 📅 st.date_input
infers the first day of the week from the user’s locale (#9706, #5215).
75 |
76 | Other Changes
77 |
78 | - 🎶 Streamlit’s CLI tool accepts array values for configuration options (#9577).
79 | - ⛓️ Static file serving supports symlinks (#9147, #9146). Thanks, link89!
80 | - 🚀 Streamlit provides helpful links for deployment when an app is running locally (#9681).
81 | - ComponentRequestHandler
allows symlinks (#9588).
84 | - 👆 Streamlit works with pillow
version 11 (#9742). Thanks, hauntsaninja!
85 | - 🗺️ Deck.gl was upgraded to version 9.0.33 (#9636).
86 | - 🦠 Bug fix: st.latex
stays center-aligned when using the help
keyword argument (#9698, #9682). Thanks, emmagarr!
87 | - 🪰 Bug fix: Apps correctly access local storage on Android (#9744, #9740).
88 | - 🕷️ Bug fix: Cached class methods can be cleared (#9642, #9633).
89 | - 🐞 Bug fix: Streamlit clears fragment auto-reruns when a user changes pages. This prevents an invalid index (#9617).
90 | - 🐝 Bug fix: st.page_link
margins are correct (#9625).
91 | - 🐜 Bug fix: Form widgets show submission instructions when in focus (#9576, #7079).
92 | - 🪲 Bug fix: st.navigation
correctly reconciles client.showSidebarNavigation
(#9589, #9581).
93 | - 🐛 Bug fix: st.text_area
requires a minimum height of 68px which fits two lines (#9561, #9217).
94 | - 💅 Bug fix: Various styling fixes (#9529, #8131, #9555, #9496, #9554, #9349, #7739).
95 |
96 | ## Version 1.39.0
97 |
98 | Release date: October 1, 2024
99 |
100 | Highlights
101 |
102 | - 🎤 Introducing st.experimental_audio_input
to let users record with their microphones!
103 | - 📍 st.pydeck_chart
can return selection events!
104 |
105 | Notable Changes
106 |
107 | - 😃 st.button
, st.download_button
, st.form_submit_button
, st.link_button
, and st.popover
each have a new parameter to add an icon.
108 | - 🏢 st.logo
has a new parameter to adjust the size of your logo.
109 | - 🧭 st.navigation
lets you display an always-expanded or collapsible menu using a new expanded
parameter.
110 | - height
and width
for st.map
and st.pydeck_chart
.
111 | - ↩️ Form submission behavior can be configured with a new enter_to_submit
parameter (#9480, #7538, #9406, #8042).
112 | - ⏱️ A new config option, server.disconnectedSessionTTL
, lets you set a minimum time before a disconnected session is cleaned up (#9179).
113 | - 🤹 Dataframes support multi-index headers (#9483, #6319).
114 |
115 | Other Changes
116 |
117 | - 🔑 Widget keys appear as HTML classes in the DOM with an st-key-
prefix (#9295, #5437, #3888).
118 | - 🔍 The StreamlitAPIException
class has been extended into more specific exceptions for some of the most common errors (#9318).
119 | - 🗺️ st.map
and st.pydeck_chart
have a full-screen toggle that matches the dataframe toolbar.
120 | - ⬆️ Frontend dependencies for Vega have been upgraded (#9443, #9438).
121 | - 🕵️ Streamlit is compatible with Watchdog version 5 (#9354). Thanks, RubenVanEldik!
122 | - 🔁 Streamlit is compatible with Tenacity version 9 (#9348).
123 | - 🔢 Bug fix: Column configuration will override any text or number format from pandas.Styler
(#9538, #7329, #7977).
124 | - 🦋 Bug fix: Deck GL zoom button has the correct border radius (#9536).
125 | - 🦐 Bug fix: Embedded apps have the correct padding to avoid hiding elements (#9524, #9341).
126 | - 🎨 Bug fix: The st.multiselect
placeholder text has the correct color (#9523, #9514).
127 | - 🧹 Bug fix: st.json
scrolls horizontally instead of overflowing its container (#9521, #9520).
128 | - 🌬️ Bug fix: Bokeh charts (temporarily) don't have a fullscreen button to prevent horizontal scrolling (#9528, #2358).
129 | - 🐡 Bug fix: Users are correctly redirected if they add a trailing slash to a page URL (#9500, #9127).
130 | - 📁 Bug fix: st.Page
warns developers against using subdirectories in url_path
, which is not supported (#9499).
131 | - 💩 Bug fix: Streamlit correctly calculates dataframe widths to prevent Minified React error #185: Maximum update depth exceeded (#9490, #7949).
132 | - ☠️ Bug fix: ScriptRunContext handles the active script hash to avoid a race condition where widgets lose state in a multipage app (#9441, #9100).
133 | - 🪱 Bug fix: PDFs don't appear as plain text when hosted through static file serving in Streamlit (#9439, #9425).
134 | - 👻 Bug fix: Fragment elements don't disappear when used with custom components and callbacks (#9381, #9389, #9372).
135 | - 👽 Bug fix: Streamlit watches the correct directory for file changes (#9453, #7467).
136 | - 🦀 Bug fix: The sidebar navigation uses page count to determine when to display a "show more" button for more consistent behavior (#9394).
137 | - 🦎 Bug fix: The internal script hash is updated at the beginning of a script run instead of the end for correct page routing when a script run is interrupted (#9408, #8975).
138 | - 🐌 Bug fix: Bold formatting in headers is ignored (#9395, #4248).
139 | - 🕸️ Bug fix: Streamlit correctly identifies the MIME type of more files to prevent custom components from not rendering (#9390, #9365). Thanks, t0mdavid-m!
140 | - 🦗 Bug fix: The client.showSidebarNavigation
configuration option works correctly with st.navigation
(#9379).
141 | - 🦂 Bug fix: Streamlit uses example.com
instead of test.com
in a health check to avoid unnecessary warnings (#9371). Thanks, wyattscarpenter!
142 | - 🦟 Bug fix: st.Page
will raise an error if it tries to initialize a page with an empty path (#9374, #8892).
143 | - 🦠 Bug fix: An unchanged st.dialog
can be programmatically reopened after a user has dismissed it (#9333, #9323).
144 | - 🪰 Bug fix: Streamlit will not remove underscores from declared page titles in st.Page
(#9375, #8890).
145 | - 🪳 Bug fix: st.logo
does not flicker when switching pages (#9361, #8815).
146 | - 🕷️ Bug fix: st.data_editor
allows users to re-add a row with the same index after deleting it (#8864, #8854).
147 | - 🐞 Bug fix: st.logo
maintains its aspect ratio when resized to fit within the sidebar width (#9368).
148 | - 🐝 Bug fix: Streamlit correctly removes st.logo
if not called during a rerun (#9337, #9336).
149 | - 🐜 Bug fix: st.logo
does not flicker when the sidebar changes its state (#9338).
150 | - 🪲 Bug fix: Streamlit renders st.balloons
and st.snow
in a React Portal for improved rendering and compatibility with st.dialog
(#9335, #9236).
151 | - 🐛 Bug fix: Option labels are cleanly truncated when st.multiselect
is displayed in a narrow container (#9334, #8213).
152 |
153 | ## Version 1.38.0
154 |
155 | Release date: August 27, 2024
156 |
157 | Highlights
158 |
159 | - 📈 Streamlit natively supports more dataframe formats! Use dataframe and series objects from popular libraries like Dask, Modin, Numpy, pandas, Polars, PyArrow, Snowpark, Xarray, and more. Use database cursors compliant with the Python Database API Specification 2.0. Use anything that supports the Python dataframe interchange protocol. See the docs.
160 |
161 | Notable Changes
162 |
163 | - st.json
elements.
164 | - 🧑💻 You can choose to wrap lines in st.code
.
165 | - 🕵️ Streamlit supports Kubernetes style secrets so you can use Snowflake Snowpark Container Services secret format (#9078).
166 | - pydantic<2.0
(#9257).
167 | - 💔 Breaking change: We removed the experimental cache replay feature from caching decorators (#9305).
168 |
169 | Other Changes
170 |
171 | - 🌐 For better app efficiency, a WebSocket reconnect will not trigger a rerun unless a script run was interrupted (#9083).
172 | - 👋 We updated our streamlit hello
app to use Google Material icons.
173 | - ⌨️ st.number_input
, st.selectbox
, st.slider
, st.select_slider
, and st.radio
provide more precise type hinting for their return values (#9048, #9296, #8717). Thanks, Asaurus1!
174 | - ⭐ st.feedback
provides more precise type hinting for its return value (#9216). Thanks, wyattscarpenter!
175 | - 💅 We improved theme management for embedded apps via postMessage
(#9103).
176 | - 🌱 Bug fix: Within the sidebar, the image for st.logo
resizes along with the sidebar width (#9298, #8707).
177 | - 🪹 Bug fix: When a parent fragment updates, Streamlit cleans up child fragments correctly (#9246, #9233, #9267).
178 | - 💩 Bug fix: Elements unstale within a fragment rerun as they are updated instead of all together at the end of the fragment rerun (#9285).
179 | - 🪱 Bug fix: If a block type changes during a rerun, Streamlit discards the child elements of that block to prevent improper visual artifacts, like st.tabs
causing a blank page (#9276, #9259, #8676).
180 | - ☠️ Bug fix: Widget state is preserved when page reruns are interrupted with another rerun (#9187, #9163). Thanks, dannyopts!
181 | - 👽 Bug fix: options
in st.selectbox
, st.multiselect
, st.radio
, and st.select_slider
correctly use dict_items
(#9241, #9237, #5377).
182 | - 👻 Bug fix: A SelectboxColumn
index will show with the correct, grayed-out styling in a dataframe (#9231, #8772).
183 | - 🦀 Bug fix: st.write_stream
will not immediately fail when receiving an empty chunk (#9234, #9227).
184 | - 🦋 Bug fix: Streamlit won't auto-scroll to an empty anchor, if present (#9206, #9203).
185 | - 🦎 Bug fix: We changed the handling of scriptRunId
to prevent st.tabs
from showing extra, empty tabs in fragments (#9186, #9158, #9215).
186 | - 🐌 Bug fix: Automatically rerunning fragments don't raise FragmentStorageKeyError
to prevent a possible race condition (#9183, #9080).
187 | - 🕸️ Bug fix: We improved st.plotly_chart
's handling of the pass-through keyword argument config
(#9190, #9134).
188 | - 🦗 Bug fix: Markdown in all label
parameters correctly ignores headers (#9189, #9141).
189 | - 🦂 Bug fix: We reverted a change to fragments which caused some widgets to lose state in some circumstances (#9178, #9171).
190 | - 🦟 Bug fix: The deprecation warnings for st.experimental_fragment
and st.experimental_dialog
only show when the commands are called. This prevents custom components which use them from raising premature warnings on import (#9170, #9143).
191 | - 🦠 Bug fix: st.code
shows syntax highlighting for diff code when language="diff"
(#9172, #8687).
192 | - 🪰 Bug fix: Streamlit commands that raise ScriptControlException
execute as expected in try-except blocks (#9167, #9155, #9182).
193 | - 🪳 Bug fix: The value
for st.date_input
has the correct type for linting (#9149). Thanks, wyattscarpenter!
194 | - 🕷️ Bug fix: We updated plotly.js
to support hoversubplots="axis"
(#9144, #9118).
195 | - 🐞 Bug fix: We stabilized the identity of st.map
instances so the command doesn't create multiple maps when its parameters are updated (#9092, #8329).
196 | - 🐝 Bug fix: You can now clear the cache for cached class instance methods (#9101, #8638).
197 | - 🐜 Bug fix: Copy buttons work correctly in dialogs (#9130, #9112).
198 | - 🪲 Bug fix: Streamlit magic works consistently in for-else, while-else, try-else, try-except, and match blocks (#9110, #9109). Thanks, whitphx!
199 | - 🐛 Bug fix: When printing an app, the bottom container will always print at the end without overlapping other content (#9129).
200 |
201 | ## Version 1.37.0
202 |
203 | Release date: July 25, 2024
204 |
205 | Highlights
206 |
207 | - 🍪 Introducing st.context
to read headers and cookies!
208 | - ⭐ Introducing st.feedback
to collect ratings and sentiment from your users!
209 | - 👟 Announcing the general availability of st.fragment
, a decorator that lets you rerun functions independently of the whole page.
210 | - 🍿 Announcing the general availability of st.dialog
, a decorator that lets you create modal dialogs.
211 |
212 | Notable Changes
213 |
214 | - ℹ️ You can use icons from the Material Symbols library in Markdown!
215 | - 📈 You can pass graphviz.Source
objects to st.graphviz_chart
.
216 | - 📊 You can modify the stacking behavior for st.bar_chart
and st.area_chart
.
217 | - 🔭 Within a fragment, you can scope st.rerun
to the fragment.
218 | - 🪺 Streamlit supports nested fragments (#8931, #8635).
219 | - 📞 Fragments can be used in callback functions (#8916, #8591).
220 |
221 | Other Changes
222 |
223 | - ⭕ Material Symbols are rounded instead of outlined (#8998).
224 | - 🔢 Streamlit supports Numpy version 2.0 (#8940).
225 | - 😄 We've updated emoji validation for new emojis (#8923).
226 | - 👻 We've removed several experimental commands with new, generally available versions (#8943).
227 | - ☠️ We've removed deprecated configuration options per their announced expiration date (#9005, #9013, #9018).
228 | - 🦎 Bug fix: Nested fragments rerun correctly when a child fragment precedes a widget in the parent fragment (#9114).
229 | - 🐌 Bug fix: Streamlit validates file paths before performing additional checks when using static file serving for improved security (#8990).
230 | - 🕸️ Bug fix: st.map
displays at the correct width inside st.expander
(#9070, #8004).
231 | - 🦗 Bug fix: Streamlit displays the correct (Windows) path for secrets.toml
in an error message (#9061, #6147).
232 | - 🦂 Bug fix: st.switch_page
correctly clears non-embed query parameters when the user switches pages (#9059, #9050).
233 | - 🦟 Bug fix: Custom themes display correctly for multipage elements like st.page_link
(#8994, #8978).
234 | - 🦠 Bug fix: st.snow
and st.balloons
don't show in prints (#9053, #7790).
235 | - 🪰 Bug fix: We've improved the default formatting for st.number_input
(#9035, #7163).
236 | - 🪳 Bug fix: An st.navigation
example was corrected (#9027, #9026). Thanks, mahotd!
237 | - 🕷️ Bug fix: Dialogs no longer have a brief delay when closing (#9023, #8747).
238 | - 🦀 Bug fix: Streamlit correctly raises a KeyError
when encountered in a fragment instead of a misleading, fragment-related error (#9011, #8494).
239 | - 🐞 Bug fix: Streamlit doesn't clear MediaFileManager
on fragment reruns to prevent invalid references (#9010, #8932).
240 | - 🐝 Bug fix: Custom themes are correctly removed when deleted (#8989, #8962).
241 | - 🐜 Bug fix: Streamlit supports non-unix style paths for correct multipage routing in Windows (#8988, #8958).
242 | - 🪲 Bug fix: Using st.rerun
in a fragment will not cause the app's main body content to render in the fragment in rare events (#8798).
243 | - 🐛 Bug fix: When an exception is raised within a fragment, Streamlit shows the error message within the fragment (#8868).
244 |
245 | ## Version 1.36.0
246 |
247 | Release date: June 20, 2024
248 |
249 | Highlights
250 |
251 | - 🧭 Introducing st.navigation
and st.Page
for a new way to define multipage apps! Check out the docs to learn more.
252 |
253 | Notable Changes
254 |
255 | - 📊 st.bar_chart
can render charts horizontally.
256 | - ℹ️ st.expander
supports adding an icon next to its label.
257 | - 🏗️ st.columns
lets you set vertical alignment.
258 | - 📲 Custom components support callback functions (#8633, #3977).
259 | - 📥 Fragments no longer support rendering widgets outside of their main body (#8756).
260 | - 🏷️ You can now customize axis labels for st.area_chart
, st.bar_chart
, st.line_chart
, and st.scatter_chart
.
261 | - ⌛ The caching parameter experimental_allow_widgets
is deprecated (#8817).
262 | - ❌ Streamlit no longer supports legacy caching. st.cache
is now an alias for st.cache_data
and st.cache_resource
(#8737).
263 | - ⬆️ Streamlit supports protobuf
version 5 (#8627).
264 |
265 | Other Changes
266 |
267 | - ✨ Streamlit Hello uses st.navigation
and st.Page
, the new, preferred method for declaring multipage apps (#8806).
268 | - 🧹 Streamlit no longer appends "· Streamlit" to the page title of apps, unless running on Community Cloud (#8900).
269 | - 🦋 Streamlit magic and st.write
use st.json
to display st.secrets
(#8659, #2905).
270 | - 🔍 Streamlit doesn't automatically check for newer version on PyPi (#8841, #8453).
271 | - 🐌 Bug fix: Custom component functions require importing streamlit.components.v1
(#8666, #8644).
272 | - 🕸️ Bug fix: Reverted change to handle Altairs resolve_scale
method since it caused a regression (#8845, #8642).
273 | - 🦗 Bug fix: Images in Markdown do not overflow the Markdown container (#8794).
274 | - 🦂 Bug fix: Clarified the error message for st.selectbox
when index
is larger than the size of options
(#8775, #8771).
275 | - 🦟 Bug fix: Streamlit correctly handles non-widget elements with IDs (#8770, #8768).
276 | - 🦠 Bug fix: Docstrings correctly identify when use_container_width=True
is the default (#8809).
277 | - 🪰 Bug fix: Streamlit has a consistent minimum element height for better vertical alignment (#8797, #8835, #8027, #8706).
278 | - 🪳 Bug fix: Added check to ensure SessionInfo
is initialized before performing actions (#8779, #8321, #7549).
279 | - 🕷️ Bug fix: Dataframe use raw numbers without formatting by default (#8708, #8695).
280 | - 🐞 Bug fix: Updated the error message for disallowed writes to Session State (#8720, #8715).
281 | - 🐝 Bug fix: Streamlit doesn't initialize LocalSourcesWatcher
if file watching is disabled (#8741, #8738).
282 | - 🐜 Bug fix: st.experimental_dialog
no longer has an invalid default value for title
(#8729).
283 | - 🪲 Bug fix: Removed deprecated kwargs in ast.Call
to prevent type error (#8711). Thanks, JelleZijlstra!
284 | - 🐛 Bug fix: st.experimental_dialog
is explicitly exported to avoid a type checking error (#8728, #8712).
285 |
286 | ## Version 1.35.0
287 |
288 | Release date: May 23, 2024
289 |
290 | Highlights
291 |
292 | - 📈 Announcing user selections for charts! Use st.plotly_chart
, st.altair_chart
, and st.vega_lite_chart
to make chart widgets for even more interactive apps.
293 | - 🚣♂️ Announcing user selections for dataframes. Get row and column selections from users with st.dataframe
.
294 | - 💼 Introducing st.logo
to add an image in the sidebar, above navigation.
295 |
296 | Notable Changes
297 |
298 | - 🔗 st.page_link
supports Material icons (#8593).
299 | - ⚓ Anchor button for headers display inline at the end of headers for a more beautiful and consistent appearance (#8587).
300 | - 🈂️ SQLConnection
accepts query
as a sqlalchemy.URL.create
parameter so you can specify character sets (#8581). Thanks, LucianLiu6!
301 |
302 | Other Changes
303 |
304 | - 🕸️ Bug fix: A fallback method was added for CSV downloads to increase browser compatibility (#8452, #8210).
305 | - 🦗 Bug fix: Column config is deep-copied when cloned to prevent unintentional modifications (#8677).
306 | - 🦂 Bug fix: st.data_editor
renders correctly when using num_rows=dynamic
with null values in added rows (#8640, #7458).
307 | - 🦟 Bug fix: streamlit run
will display the localhost
address when initializing Streamlit with server.headless=true
(#8647, #8629).
308 | - 🦠 Bug fix: Scroll margin matches the new toolbar (app chrome) height (#8641, #8554).
309 | - 🪰 Bug fix: Enum coercion is compatible with StrEnum (#8622, #8500). Thanks, 97k!
310 | - 🪳 Bug fix: Focus is returned to chat input after clicking submit for a better mobile experience (#8637).
311 | - 🕷️ Bug fix: Internal parameter and view names for Altair charts are stabilized for better performance (#8628).
312 | - 🐞 Bug fix: Typing was improved for st.query_params.update()
and st.query_params.from_dict()
(#8614, #8613). Thanks, Asaurus1!
313 | - 🐝 Bug fix: The fullscreen button no longer appears for st.table
to prevent unwanted side scrolling (#8621, #2358).
314 | - 🐜 Bug fix: Streamlit correctly clears stale elements when using st.rerun
(#8599, #8360).
315 | - 🪲 Bug fix: Custom components can be executed standalone for testing and scripting (#8620, #8606).
316 | - 👻 Bug fix: Plotly charts no longer render cached data when updated (#8191, #5902)
317 | - 👽 Plotly chart widths will not overflow its parent container when rendered in a bordered container (#8191, #8244).
318 | - 🦀 Plotly charts using webgl
render correctly on M1/M2 chipsets for macOS (#8191, #8169).
319 | - 🦋 Plotly charts are sized correctly when rendered vertically adjacent (#8191, #7597).
320 | - 🦎 Bug fix: Plotly charts retain their state when the app window is resized (#8191, #6324).
321 | - 🐛 Bug fix: Plotly charts in st.tabs
no longer flicker when changing tabs (#8191, #8575).
322 | - 🐌 Bug fix: Plotly charts respect use_container_width
if this parameter is changed between reruns (#8191, #8576).
323 |
324 | ## Version 1.34.0
325 |
326 | Release date: May 2, 2024
327 |
328 | Highlights
329 |
330 | - 🍿 Introducing st.experimental_dialog
! Create a modal overlay that can also rerun independently from the rest of your app. Check out the docs to learn how.
331 |
332 | **Notable Ch