Skip to content

Instantly share code, notes, and snippets.

@5shekel
Created May 13, 2025 07:15
Show Gist options
  • Save 5shekel/3f6add7c27a3a9fe2ecc78279ef456b1 to your computer and use it in GitHub Desktop.
Save 5shekel/3f6add7c27a3a9fe2ecc78279ef456b1 to your computer and use it in GitHub Desktop.

└── 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

/content/develop/_index.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 |


/content/develop/api-reference/_index.md:

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 |

st.write

26 | 27 | Write arguments to the app. 28 | 29 | python 30 | st.write("Hello **world**!") 31 | st.write(my_data_frame) 32 | st.write(my_mpl_figure) 33 | 34 | 35 | 36 | 37 | 38 |

st.write_stream

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 |

Magic

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 |

screenshot

71 | 72 |

Markdown

73 | 74 | Display string formatted as Markdown. 75 | 76 | python 77 | st.markdown("Hello **world**!") 78 | 79 | 80 | 81 | 82 | 83 |

screenshot

84 | 85 |

Title

86 | 87 | Display text in title formatting. 88 | 89 | python 90 | st.title("The app title") 91 | 92 | 93 | 94 | 95 | 96 |

screenshot

97 | 98 |

Header

99 | 100 | Display text in header formatting. 101 | 102 | python 103 | st.header("This is a header") 104 | 105 | 106 | 107 | 108 | 109 |

screenshot

110 | 111 |

Subheader

112 | 113 | Display text in subheader formatting. 114 | 115 | python 116 | st.subheader("This is a subheader") 117 | 118 | 119 | 120 | 121 | 122 |

screenshot

123 | 124 |

Badge

125 | 126 | Display a small, colored badge. 127 | 128 | python 129 | st.badge("New") 130 | 131 | 132 | 133 | 134 | 135 |

screenshot

136 | 137 |

Caption

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 |

screenshot

149 | 150 |

Code block

151 | 152 | Display a code block with optional syntax highlighting. 153 | 154 | python 155 | st.code("a = 1234") 156 | 157 | 158 | 159 | 160 | 161 |

screenshot

162 | 163 |

Echo

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 |

screenshot

176 | 177 |

LaTeX

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 |

screenshot

189 | 190 |

Preformatted text

191 | 192 | Write fixed-width and preformatted text. 193 | 194 | python 195 | st.text("Hello world") 196 | 197 | 198 | 199 | 200 | 201 |

screenshot

202 | 203 |

Divider

204 | 205 | Display a horizontal rule. 206 | 207 | python 208 | st.divider() 209 | 210 | 211 | 212 | 213 | 214 |

Get help

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 |

Render HTML

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 |

screenshot

241 | 242 |

Annotated text

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 |

screenshot

255 | 256 |

Drawable Canvas

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 |

screenshot

269 | 270 |

Tags

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 |

screenshot

283 | 284 |

NLU

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 |

screenshot

297 | 298 |

Streamlit Extras

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 |

screenshot

316 | 317 |

Dataframes

318 | 319 | Display a dataframe as an interactive table. 320 | 321 | python 322 | st.dataframe(my_data_frame) 323 | 324 | 325 | 326 | 327 | 328 |

screenshot

329 | 330 |

Data editor

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 |

screenshot

342 | 343 |

Column configuration

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 |

screenshot

355 | 356 |

Static tables

357 | 358 | Display a static table. 359 | 360 | python 361 | st.table(my_data_frame) 362 | 363 | 364 | 365 | 366 |

screenshot

367 | 368 |

Metrics

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 |

screenshot

379 | 380 |

Dicts and JSON

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 |

screenshot

396 | 397 |

Streamlit Aggrid

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 |

screenshot

413 | 414 |

Streamlit Folium

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 |

screenshot

430 | 431 |

Pandas Profiling

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 |

screenshot

447 | 448 |

Image Coordinates

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 |

screenshot

464 | 465 |

Plotly Events

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 |

screenshot

481 | 482 |

Streamlit Extras

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 |

screenshot

505 | 506 |

Simple area charts

507 | 508 | Display an area chart. 509 | 510 | python 511 | st.area_chart(my_data_frame) 512 | 513 | 514 | 515 | 516 |

screenshot

517 | 518 |

Simple bar charts

519 | 520 | Display a bar chart. 521 | 522 | python 523 | st.bar_chart(my_data_frame) 524 | 525 | 526 | 527 | 528 |

screenshot

529 | 530 |

Simple line charts

531 | 532 | Display a line chart. 533 | 534 | python 535 | st.line_chart(my_data_frame) 536 | 537 | 538 | 539 | 540 |

screenshot

541 | 542 |

Simple scatter charts

543 | 544 | Display a line chart. 545 | 546 | python 547 | st.scatter_chart(my_data_frame) 548 | 549 | 550 | 551 | 552 |

screenshot

553 | 554 |

Scatterplots on maps

555 | 556 | Display a map with points on it. 557 | 558 | python 559 | st.map(my_data_frame) 560 | 561 | 562 | 563 | 564 |

screenshot

565 | 566 |

Matplotlib

567 | 568 | Display a matplotlib.pyplot figure. 569 | 570 | python 571 | st.pyplot(my_mpl_figure) 572 | 573 | 574 | 575 | 576 |

screenshot

577 | 578 |

Altair

579 | 580 | Display a chart using the Altair library. 581 | 582 | python 583 | st.altair_chart(my_altair_chart) 584 | 585 | 586 | 587 | 588 |

screenshot

589 | 590 |

Vega-Lite

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 |

screenshot

601 | 602 |

Plotly

603 | 604 | Display an interactive Plotly chart. 605 | 606 | python 607 | st.plotly_chart(my_plotly_chart) 608 | 609 | 610 | 611 | 612 |

screenshot

613 | 614 |

Bokeh

615 | 616 | Display an interactive Bokeh chart. 617 | 618 | python 619 | st.bokeh_chart(my_bokeh_chart) 620 | 621 | 622 | 623 | 624 |

screenshot

625 | 626 |

PyDeck

627 | 628 | Display a chart using the PyDeck library. 629 | 630 | python 631 | st.pydeck_chart(my_pydeck_chart) 632 | 633 | 634 | 635 | 636 |

screenshot

637 | 638 |

GraphViz

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 |

screenshot

654 | 655 |

Plost

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 |

screenshot

669 | 670 |

HiPlot

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 |

screenshot

684 | 685 |

ECharts

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 |

screenshot

699 | 700 |

Streamlit Folium

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 |

screenshot

714 | 715 |

Spacy-Streamlit

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 |

screenshot

729 | 730 |

Streamlit Agraph

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 |

screenshot

744 | 745 |

Streamlit Lottie

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 |

screenshot

759 | 760 |

Plotly Events

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 |

screenshot

774 | 775 |

Streamlit Extras

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 |

screenshot

796 | 797 |

Button

798 | 799 | Display a button widget. 800 | 801 | python 802 | clicked = st.button("Click me") 803 | 804 | 805 | 806 | 807 | 808 |

screenshot

809 | 810 |

Download button

811 | 812 | Display a download button widget. 813 | 814 | python 815 | st.download_button("Download file", file) 816 | 817 | 818 | 819 | 820 | 821 |

screenshot

822 | 823 |

Form button

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 |

screenshot

835 | 836 |

Link button

837 | 838 | Display a link button. 839 | 840 | python 841 | st.link_button("Go to gallery", url) 842 | 843 | 844 | 845 | 846 | 847 |

screenshot

848 | 849 |

Page link

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 |

screenshot

862 | 863 |

Checkbox

864 | 865 | Display a checkbox widget. 866 | 867 | python 868 | selected = st.checkbox("I agree") 869 | 870 | 871 | 872 | 873 | 874 |

screenshot

875 | 876 |

Color picker

877 | 878 | Display a color picker widget. 879 | 880 | python 881 | color = st.color_picker("Pick a color") 882 | 883 | 884 | 885 | 886 | 887 |

screenshot

888 | 889 |

Feedback

890 | 891 | Display a rating or sentiment button group. 892 | 893 | python 894 | st.feedback("stars") 895 | 896 | 897 | 898 | 899 | 900 |

screenshot

901 | 902 |

Multiselect

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 |

screenshot

914 | 915 |

Pills

916 | 917 | Display a pill-button selection widget. 918 | 919 | python 920 | st.pills("Tags", ["Sports", "AI", "Politics"]) 921 | 922 | 923 | 924 | 925 | 926 |

screenshot

927 | 928 |

Radio

929 | 930 | Display a radio button widget. 931 | 932 | python 933 | choice = st.radio("Pick one", ["cats", "dogs"]) 934 | 935 | 936 | 937 | 938 | 939 |

screenshot

940 | 941 |

Segmented control

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 |

screenshot

953 | 954 |

Selectbox

955 | 956 | Display a select widget. 957 | 958 | python 959 | choice = st.selectbox("Pick one", ["cats", "dogs"]) 960 | 961 | 962 | 963 | 964 | 965 |

screenshot

966 | 967 |

Select-slider

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 |

screenshot

979 | 980 |

Toggle

981 | 982 | Display a toggle widget. 983 | 984 | python 985 | activated = st.toggle("Activate") 986 | 987 | 988 | 989 | 990 | 991 |

screenshot

992 | 993 |

Number input

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 |

screenshot

1005 | 1006 |

Slider

1007 | 1008 | Display a slider widget. 1009 | 1010 | python 1011 | number = st.slider("Pick a number", 0, 100) 1012 | 1013 | 1014 | 1015 | 1016 | 1017 |

screenshot

1018 | 1019 |

Date input

1020 | 1021 | Display a date input widget. 1022 | 1023 | python 1024 | date = st.date_input("Your birthday") 1025 | 1026 | 1027 | 1028 | 1029 | 1030 |

screenshot

1031 | 1032 |

Time input

1033 | 1034 | Display a time input widget. 1035 | 1036 | python 1037 | time = st.time_input("Meeting time") 1038 | 1039 | 1040 | 1041 | 1042 | 1043 |

screenshot

1044 | 1045 |

Chat input

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 |

screenshot

1059 | 1060 |

Text-area

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 |

screenshot

1072 | 1073 |

Text input

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 |

screenshot

1085 | 1086 |

Audio input

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 |

screenshot

1098 | 1099 |

Data editor

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 |

screenshot

1111 | 1112 |

File uploader

1113 | 1114 | Display a file uploader widget. 1115 | 1116 | python 1117 | data = st.file_uploader("Upload a CSV") 1118 | 1119 | 1120 | 1121 | 1122 | 1123 |

screenshot

1124 | 1125 |

Camera input

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 |

screenshot

1141 | 1142 |

Streamlit Elements

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 |

screenshot

1158 | 1159 |

Tags

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 |

screenshot

1175 | 1176 |

Stqdm

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 |

screenshot

1192 | 1193 |

Timeline

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 |

screenshot

1209 | 1210 |

Camera input live

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 |

screenshot

1226 | 1227 |

Streamlit Ace

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 |

screenshot

1243 | 1244 |

Streamlit Chat

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 |

screenshot

1260 | 1261 |

Streamlit Option Menu

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 |

screenshot

1277 | 1278 |

Streamlit Extras

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 |

screenshot

1301 | 1302 |

Image

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 |

screenshot

1317 | 1318 |

Logo

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 |

screenshot

1330 | 1331 |

Audio

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 |

screenshot

1346 | 1347 |

Video

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 |

screenshot

1366 | 1367 |

Streamlit Webrtc

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 |

screenshot

1382 | 1383 |

Drawable Canvas

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 |

screenshot

1398 | 1399 |

Image Comparison

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 |

screenshot

1414 | 1415 |

Streamlit Cropper

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 |

screenshot

1430 | 1431 |

Image Coordinates

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 |

screenshot

1446 | 1447 |

Streamlit Lottie

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 |

screenshot

1469 | 1470 |

Columns

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 |

screenshot

1484 | 1485 |

Container

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 |

screenshot

1500 | 1501 |

Modal dialog

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 |

screenshot

1516 | 1517 |

Empty

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 |

screenshot

1532 | 1533 |

Expander

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 |

screenshot

1546 | 1547 |

Popover

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 |

screenshot

1560 | 1561 |

Sidebar

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 |

screenshot

1574 | 1575 |

Tabs

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 |

screenshot

1593 | 1594 |

Streamlit Elements

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 |

screenshot

1610 | 1611 |

Pydantic

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 |

screenshot

1627 | 1628 |

Streamlit Pages

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 |

screenshot

1655 | 1656 |

Chat input

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 |

screenshot

1670 | 1671 |

Chat message

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 |

screenshot

1686 | 1687 |

Status container

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 |

st.write_stream

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 |

screenshot

1719 | 1720 |

Progress bar

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 |

screenshot

1734 | 1735 |

Spinner

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 |

screenshot

1748 | 1749 |

Status container

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 |

screenshot

1762 | 1763 |

Toast

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 |

screenshot

1775 | 1776 |

Balloons

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 |

screenshot

1791 | 1792 |

Snowflakes

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 |

screenshot

1807 | 1808 |

Success box

1809 | 1810 | Display a success message. 1811 | 1812 | python 1813 | st.success("Match found!") 1814 | 1815 | 1816 | 1817 | 1818 | 1819 |

screenshot

1820 | 1821 |

Info box

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 |

screenshot

1833 | 1834 |

Warning box

1835 | 1836 | Display warning message. 1837 | 1838 | python 1839 | st.warning("Unable to fetch image. Skipping...") 1840 | 1841 | 1842 | 1843 | 1844 | 1845 |

screenshot

1846 | 1847 |

Error box

1848 | 1849 | Display error message. 1850 | 1851 | python 1852 | st.error("We encountered an error") 1853 | 1854 | 1855 | 1856 | 1857 | 1858 |

screenshot

1859 | 1860 |

Exception output

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 |

screenshot

1878 | 1879 |

Stqdm

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 |

screenshot

1895 | 1896 |

Custom notification box

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 |

screenshot

1912 | 1913 |

Streamlit Extras

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 |

Log in a user

1938 | 1939 | st.login() starts an authentication flow with an identity provider. 1940 | 1941 | python 1942 | st.login() 1943 | 1944 | 1945 | 1946 | 1947 | 1948 |

Log out a user

1949 | 1950 | st.logout() removes a user's identity information. 1951 | 1952 | python 1953 | st.logout() 1954 | 1955 | 1956 | 1957 | 1958 | 1959 |

User info

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 |

screenshot

1980 | 1981 |

Navigation

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 |

screenshot

1998 | 1999 |

Page

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 |

screenshot

2016 | 2017 |

Page link

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 |

Switch page

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 |

screenshot

2050 | 2051 |

Modal dialog

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 |

Forms

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 |

Fragments

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 |

Rerun script

2094 | 2095 | Rerun the script immediately. 2096 | 2097 | python 2098 | st.rerun() 2099 | 2100 | 2101 | 2102 | 2103 | 2104 |

Stop execution

2105 | 2106 | Stops execution immediately. 2107 | 2108 | python 2109 | st.stop() 2110 | 2111 | 2112 | 2113 | 2114 | 2115 | 2116 | 2117 | 2118 | 2119 |

screenshot

2120 | 2121 |

Autorefresh

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 |

screenshot

2137 | 2138 |

Pydantic

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 |

screenshot

2154 | 2155 |

Streamlit Pages

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 |

Cache data

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 |

Cache resource

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 |

Session state

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 |

Query parameters

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 |

Context

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 |

screenshot

2256 | 2257 |

Create a connection

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 |

screenshot

2277 | 2278 |

SnowflakeConnection

2279 | 2280 | A connection to Snowflake. 2281 | 2282 | python 2283 | conn = st.connection('snowflake') 2284 | 2285 | 2286 | 2287 | 2288 | 2289 | 2290 |

screenshot

2291 | 2292 |

SQLConnection

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 |

Connection base class

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 |

Secrets singleton

2331 | 2332 | Access secrets from a local TOML file. 2333 | 2334 | python 2335 | key = st.secrets["OpenAI_key"] 2336 | 2337 | 2338 | 2339 | 2340 | 2341 |

Secrets file

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 |

screenshot

2358 | 2359 |

Authenticator

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 |

screenshot

2375 | 2376 |

WS localStorage

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 |

screenshot

2392 | 2393 |

Streamlit Auth0

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 |

Declare a component

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 |

HTML

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 |

iframe

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 |

Configuration file

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 |

Get config option

2484 | 2485 | Retrieve a single configuration option. 2486 | 2487 | python 2488 | st.get_option("theme.primaryColor") 2489 | 2490 | 2491 | 2492 | 2493 | 2494 |

Set config option

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 |

Set page title, favicon, and more

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 |

st.testing.v1.AppTest

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 |

AppTest.from_file

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 |

AppTest.from_string

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 |

AppTest.from_function

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 |

Block

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 |

Element

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 |

Button

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 |

ChatInput

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 |

Checkbox

2658 | 2659 | A representation of st.checkbox. 2660 | 2661 | python 2662 | at.checkbox[0].check().run() 2663 | 2664 | 2665 | 2666 | 2667 | 2668 | 2669 |

ColorPicker

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 |

DateInput

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 |

Multiselect

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 |

NumberInput

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 |

Radio

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 |

SelectSlider

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 |

Selectbox

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 |

Slider

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 |

TextArea

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 |

TextInput

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 |

TimeInput

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 |

Toggle

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 |

screenshot

2819 | 2820 |

Pandas Profiling

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 |

screenshot

2836 | 2837 |

Streamlit Ace

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 |

screenshot

2853 | 2854 |

Streamlit Analytics

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 |


/content/develop/api-reference/caching-and-state/_index.md:

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 |

Cache data

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 |

Cache resource

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 |

Context

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 |

Session State

72 | 73 | Save data between reruns and across pages. 74 | 75 | python 76 | st.session_state["foo"] = "bar" 77 | 78 | 79 | 80 | 81 | 82 |

Query parameters

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 |

Get query parameters

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 |

Set query parameters

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 |


/content/develop/api-reference/caching-and-state/cache-data.md:

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 |


/content/develop/api-reference/caching-and-state/cache-resource.md:

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 |


/content/develop/api-reference/caching-and-state/context.md:

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 |


/content/develop/api-reference/caching-and-state/experimental_get_query_params.md:

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 |


/content/develop/api-reference/caching-and-state/experimental_set_query_params.md:

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 |


/content/develop/api-reference/caching-and-state/query_params.md:

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 |


/content/develop/api-reference/caching-and-state/session_state.md:

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 | state-uninitialized-exception 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 | state-clear-cache 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 | UnserializableSessionStateError 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 | state-modified-instantiated-exception 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 | state-value-api-exception 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 | state-button-exception 225 |


/content/develop/api-reference/charts/_index.md:

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 | screenshot 22 | 23 |

Simple area charts

24 | 25 | Display an area chart. 26 | 27 | python 28 | st.area_chart(my_data_frame) 29 | 30 | 31 | 32 | 33 |

screenshot

34 | 35 |

Simple bar charts

36 | 37 | Display a bar chart. 38 | 39 | python 40 | st.bar_chart(my_data_frame) 41 | 42 | 43 | 44 | 45 |

screenshot

46 | 47 |

Simple line charts

48 | 49 | Display a line chart. 50 | 51 | python 52 | st.line_chart(my_data_frame) 53 | 54 | 55 | 56 | 57 |

screenshot

58 | 59 |

Simple scatter charts

60 | 61 | Display a line chart. 62 | 63 | python 64 | st.scatter_chart(my_data_frame) 65 | 66 | 67 | 68 | 69 |

screenshot

70 | 71 |

Scatterplots on maps

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 |

screenshot

87 | 88 |

Matplotlib

89 | 90 | Display a matplotlib.pyplot figure. 91 | 92 | python 93 | st.pyplot(my_mpl_figure) 94 | 95 | 96 | 97 | 98 |

screenshot

99 | 100 |

Altair

101 | 102 | Display a chart using the Altair library. 103 | 104 | python 105 | st.altair_chart(my_altair_chart) 106 | 107 | 108 | 109 | 110 |

screenshot

111 | 112 |

Vega-Lite

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 |

screenshot

123 | 124 |

Plotly

125 | 126 | Display an interactive Plotly chart. 127 | 128 | python 129 | st.plotly_chart(my_plotly_chart) 130 | 131 | 132 | 133 | 134 |

screenshot

135 | 136 |

Bokeh

137 | 138 | Display an interactive Bokeh chart. 139 | 140 | python 141 | st.bokeh_chart(my_bokeh_chart) 142 | 143 | 144 | 145 | 146 |

screenshot

147 | 148 |

PyDeck

149 | 150 | Display a chart using the PyDeck library. 151 | 152 | python 153 | st.pydeck_chart(my_pydeck_chart) 154 | 155 | 156 | 157 | 158 |

screenshot

159 | 160 |

GraphViz

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 |

screenshot

176 | 177 |

Plost

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 |

screenshot

191 | 192 |

HiPlot

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 |

screenshot

206 | 207 |

ECharts

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 |

screenshot

221 | 222 |

Streamlit Folium

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 |

screenshot

236 | 237 |

Spacy-Streamlit

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 |

screenshot

251 | 252 |

Streamlit Agraph

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 |

screenshot

266 | 267 |

Streamlit Lottie

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 |

screenshot

281 | 282 |

Plotly Events

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 |

screenshot

296 | 297 |

Streamlit Extras

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 |


/content/develop/api-reference/charts/altair_chart.md:

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 |


/content/develop/api-reference/charts/area_chart.md:

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 |


/content/develop/api-reference/charts/bar_chart.md:

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 |


/content/develop/api-reference/charts/bokeh_chart.md:

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 |


/content/develop/api-reference/charts/graphviz_chart.md:

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 |


/content/develop/api-reference/charts/line_chart.md:

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 |


/content/develop/api-reference/charts/map.md:

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 |


/content/develop/api-reference/charts/plotly_chart.md:

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 |


/content/develop/api-reference/charts/pydeck_chart.md:

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 |


/content/develop/api-reference/charts/pyplot.md:

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 |


/content/develop/api-reference/charts/scatter_chart.md:

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 |


/content/develop/api-reference/charts/vega_lite_chart.md:

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 |


/content/develop/api-reference/chat/_index.md:

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 | screenshot 16 | 17 |

Chat input

18 | 19 | Display a chat input widget. 20 | 21 | 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 |

screenshot

31 | 32 |

Chat message

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 |

screenshot

47 | 48 |

Status container

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 |

st.write_stream

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 |


/content/develop/api-reference/chat/chat-input.md:

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 |


/content/develop/api-reference/chat/chat-message.md:

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 |


/content/develop/api-reference/command-line/_index.md:

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 |


/content/develop/api-reference/command-line/cache.md:

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 |


/content/develop/api-reference/command-line/config.md:

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 |


/content/develop/api-reference/command-line/docs.md:

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 |


/content/develop/api-reference/command-line/hello.md:

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 |


/content/develop/api-reference/command-line/help.md:

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 |


/content/develop/api-reference/command-line/init.md:

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 |


/content/develop/api-reference/command-line/run.md:

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 |


/content/develop/api-reference/command-line/version.md:

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 |


/content/develop/api-reference/configuration/_index.md:

1 | --- 2 | title: Configuration 3 | slug: /develop/api-reference/configuration 4 | --- 5 | 6 | # Configuration 7 | 8 | 9 | 10 | 11 |

Configuration file

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 |

Get config option

26 | 27 | Retrieve a single configuration option. 28 | 29 | python 30 | st.get_option("theme.primaryColor") 31 | 32 | 33 | 34 | 35 | 36 |

Set config option

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 |

Set page title, favicon, and more

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 |


/content/develop/api-reference/configuration/config-toml.md:

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 |


/content/develop/api-reference/configuration/get_option.md:

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 |


/content/develop/api-reference/configuration/set_option.md:

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 |


/content/develop/api-reference/configuration/set_page_config.md:

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 |


/content/develop/api-reference/connections/_index.md:

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 | screenshot 14 | 15 |

Create a connection

16 | 17 | Connect to a data source or API 18 | 19 | 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 |

screenshot

35 | 36 |

SnowflakeConnection

37 | 38 | A connection to Snowflake. 39 | 40 | python 41 | conn = st.connection('snowflake') 42 | 43 | 44 | 45 | 46 | 47 | 48 |

screenshot

49 | 50 |

SQLConnection

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 |

Connection base class

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 |

Secrets singleton

88 | 89 | Access secrets from a local TOML file. 90 | 91 | python 92 | key = st.secrets["OpenAI_key"] 93 | 94 | 95 | 96 | 97 | 98 |

Secrets file

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 |

SnowparkConnection

116 | 117 | A connection to Snowflake. 118 | 119 | python 120 | conn = st.connection("snowpark") 121 | 122 | 123 | 124 | 125 | 126 |


/content/develop/api-reference/connections/connection.md:

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 |


/content/develop/api-reference/connections/connections-baseconnection.md:

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 |


/content/develop/api-reference/connections/connections-experimentalbaseconnection.md:

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 |


/content/develop/api-reference/connections/connections-snowflake.md:

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 |


/content/develop/api-reference/connections/connections-snowpark.md:

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 |


/content/develop/api-reference/connections/connections-sql.md:

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 |


/content/develop/api-reference/connections/experimental-connection.md:

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 |


/content/develop/api-reference/connections/secrets-toml.md:

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 |


/content/develop/api-reference/connections/secrets.md:

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 |


/content/develop/api-reference/control-flow/_index.md:

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 | screenshot 17 | 18 |

Modal dialog

19 | 20 | Insert a modal dialog that can rerun independently from the rest of the script. 21 | 22 | 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 |

Fragments

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 |

Rerun script

49 | 50 | Rerun the script immediately. 51 | 52 | python 53 | st.rerun() 54 | 55 | 56 | 57 | 58 | 59 | 60 |

Stop execution

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 |

Forms

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 |

Form submit button

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 |

screenshot

116 | 117 |

Autorefresh

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 |

screenshot

133 | 134 |

Pydantic

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 |

screenshot

150 | 151 |

Streamlit Pages

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 |


/content/develop/api-reference/control-flow/dialog.md:

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 |


/content/develop/api-reference/control-flow/experimental_rerun.md:

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 |


/content/develop/api-reference/control-flow/form.md:

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 |


/content/develop/api-reference/control-flow/form_submit_button.md:

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 |


/content/develop/api-reference/control-flow/fragment.md:

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 |


/content/develop/api-reference/control-flow/rerun.md:

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 |


/content/develop/api-reference/control-flow/stop.md:

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 |


/content/develop/api-reference/custom-components/_index.md:

1 | --- 2 | title: Custom components 3 | slug: /develop/api-reference/custom-components 4 | --- 5 | 6 | # Custom components 7 | 8 | 9 | 10 | 11 | 12 |

Declare a component

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 |

HTML

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 |

iframe

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 |


/content/develop/api-reference/custom-components/declare_component.md:

1 | --- 2 | title: st.components.v1.declare_component 3 | slug: /develop/api-reference/custom-components/st.components.v1.declare_component 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/custom-components/html.md:

1 | --- 2 | title: st.components.v1.html 3 | slug: /develop/api-reference/custom-components/st.components.v1.html 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/custom-components/iframe.md:

1 | --- 2 | title: st.components.v1.iframe 3 | slug: /develop/api-reference/custom-components/st.components.v1.iframe 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/_index.md:

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 | screenshot 18 | 19 |

Dataframes

20 | 21 | Display a dataframe as an interactive table. 22 | 23 | python 24 | st.dataframe(my_data_frame) 25 | 26 | 27 | 28 | 29 | 30 |

screenshot

31 | 32 |

Data editor

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 |

screenshot

44 | 45 |

Column configuration

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 |

screenshot

57 | 58 |

Static tables

59 | 60 | Display a static table. 61 | 62 | python 63 | st.table(my_data_frame) 64 | 65 | 66 | 67 | 68 |

screenshot

69 | 70 |

Metrics

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 |

screenshot

81 | 82 |

Dicts and JSON

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 |

screenshot

98 | 99 |

Streamlit Aggrid

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 |

screenshot

115 | 116 |

Streamlit Folium

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 |

screenshot

132 | 133 |

Pandas Profiling

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 |

screenshot

149 | 150 |

Image Coordinates

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 |

screenshot

166 | 167 |

Plotly Events

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 |

screenshot

183 | 184 |

Streamlit Extras

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 |


/content/develop/api-reference/data/column_config/_index.md:

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 | screenshot 15 | 16 |

Column

17 | 18 | Configure a generic column. 19 | 20 | python 21 | Column("Streamlit Widgets", width="medium", help="Streamlit **widget** commands 🎈") 22 | 23 | 24 | 25 | 26 |

screenshot

27 | 28 |

Text column

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 |

screenshot

40 | 41 |

Number column

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 |

screenshot

53 | 54 |

Checkbox column

55 | 56 | Configure a checkbox column. 57 | 58 | python 59 | CheckboxColumn("Your favorite?", help="Select your **favorite** widgets") 60 | 61 | 62 | 63 | 64 | 65 |

screenshot

66 | 67 |

Selectbox column

68 | 69 | Configure a selectbox column. 70 | 71 | python 72 | SelectboxColumn("App Category", options=["🤖 LLM", "📈 Data Viz"]) 73 | 74 | 75 | 76 | 77 | 78 |

screenshot

79 | 80 |

Datetime column

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 |

screenshot

92 | 93 |

Date column

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 |

screenshot

105 | 106 |

Time column

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 |

screenshot

117 | 118 |

JSON column

119 | 120 | Configure a JSON column. 121 | 122 | python 123 | JSONColumn("Properties", width="medium") 124 | 125 | 126 | 127 | 128 |

screenshot

129 | 130 |

List column

131 | 132 | Configure a list column. 133 | 134 | python 135 | ListColumn("Sales (last 6 months)", width="medium") 136 | 137 | 138 | 139 | 140 | 141 |

screenshot

142 | 143 |

Link column

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 |

screenshot

155 | 156 |

Image column

157 | 158 | Configure an image column. 159 | 160 | python 161 | ImageColumn("Preview Image", help="The preview screenshots") 162 | 163 | 164 | 165 | 166 | 167 |

screenshot

168 | 169 |

Area chart column

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 |

screenshot

181 | 182 |

Line chart column

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 |

screenshot

194 | 195 |

Bar chart column

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 |

screenshot

207 | 208 |

Progress column

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 |


/content/develop/api-reference/data/column_config/areachartcolumn.md:

1 | --- 2 | title: st.column_config.AreaChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.areachartcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/barchartcolumn.md:

1 | --- 2 | title: st.column_config.BarChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.barchartcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/checkboxcolumn.md:

1 | --- 2 | title: st.column_config.CheckboxColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.checkboxcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/column.md:

1 | --- 2 | title: st.column_config.Column 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.column 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/datecolumn.md:

1 | --- 2 | title: st.column_config.DateColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.datecolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/datetimecolumn.md:

1 | --- 2 | title: st.column_config.DatetimeColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.datetimecolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/imagecolumn.md:

1 | --- 2 | title: st.column_config.ImageColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.imagecolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/jsoncolumn.md:

1 | --- 2 | title: st.column_config.JsonColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.jsoncolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/linechartcolumn.md:

1 | --- 2 | title: st.column_config.LineChartColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.linechartcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/linkcolumn.md:

1 | --- 2 | title: st.column_config.LinkColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.linkcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/listcolumn.md:

1 | --- 2 | title: st.column_config.ListColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.listcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/numbercolumn.md:

1 | --- 2 | title: st.column_config.NumberColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.numbercolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/progresscolumn.md:

1 | --- 2 | title: st.column_config.ProgressColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.progresscolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/selectboxcolumn.md:

1 | --- 2 | title: st.column_config.SelectboxColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.selectboxcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/textcolumn.md:

1 | --- 2 | title: st.column_config.TextColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.textcolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/column_config/timecolumn.md:

1 | --- 2 | title: st.column_config.TimeColumn 3 | slug: /develop/api-reference/data/st.column_config/st.column_config.timecolumn 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/data/data_editor.md:

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 |


/content/develop/api-reference/data/dataframe.md:

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 |


/content/develop/api-reference/data/experimental_data_editor.md:

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 |


/content/develop/api-reference/data/json.md:

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 |


/content/develop/api-reference/data/metric.md:

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 |


/content/develop/api-reference/data/table.md:

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 |


/content/develop/api-reference/layout/_index.md:

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 | screenshot 16 | 17 |

Columns

18 | 19 | Insert containers laid out as side-by-side columns. 20 | 21 | 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 |

screenshot

31 | 32 |

Container

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 |

screenshot

47 | 48 |

Modal dialog

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 |

screenshot

63 | 64 |

Empty

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 |

screenshot

79 | 80 |

Expander

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 |

screenshot

93 | 94 |

Popover

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 |

screenshot

107 | 108 |

Sidebar

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 |

screenshot

121 | 122 |

Tabs

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 |

screenshot

140 | 141 |

Streamlit Elements

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 |

screenshot

157 | 158 |

Pydantic

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 |

screenshot

174 | 175 |

Streamlit Pages

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 |


/content/develop/api-reference/layout/columns.md:

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 |


/content/develop/api-reference/layout/container.md:

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 |


/content/develop/api-reference/layout/empty.md:

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 |


/content/develop/api-reference/layout/expander.md:

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 |


/content/develop/api-reference/layout/popover.md:

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 |


/content/develop/api-reference/layout/sidebar.md:

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! ↔️ 31 | 32 | 33 | 34 | Here's an example of how you'd add a selectbox and a radio button to your sidebar: 35 | 36 | 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 |


/content/develop/api-reference/layout/tabs.md:

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 |


/content/develop/api-reference/media/_index.md:

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 | screenshot 14 | 15 |

Image

16 | 17 | Display an image or list of images. 18 | 19 | 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 |

screenshot

30 | 31 |

Logo

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 |

screenshot

43 | 44 |

Audio

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 |

screenshot

59 | 60 |

Video

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 |

screenshot

79 | 80 |

Streamlit Webrtc

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 |

screenshot

95 | 96 |

Drawable Canvas

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 |

screenshot

111 | 112 |

Image Comparison

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 |

screenshot

127 | 128 |

Streamlit Cropper

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 |

screenshot

143 | 144 |

Image Coordinates

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 |

screenshot

159 | 160 |

Streamlit Lottie

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 |


/content/develop/api-reference/media/audio.md:

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 |


/content/develop/api-reference/media/image.md:

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 |


/content/develop/api-reference/media/logo.md:

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 |


/content/develop/api-reference/media/video.md:

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 |


/content/develop/api-reference/navigation/_index.md:

1 | --- 2 | title: Navigation and pages 3 | slug: /develop/api-reference/navigation 4 | --- 5 | 6 | # Navigation and pages 7 | 8 | 9 | 10 | 11 | 12 | screenshot 13 | 14 |

Navigation

15 | 16 | Configure the available pages in a multipage app. 17 | 18 | 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 |

screenshot

31 | 32 |

Page

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 |

screenshot

49 | 50 |

Page link

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 |

Switch page

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 |


/content/develop/api-reference/navigation/navigation.md:

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 |


/content/develop/api-reference/navigation/page.md:

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 |


/content/develop/api-reference/navigation/switch_page.md:

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 |


/content/develop/api-reference/status/_index.md:

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 | screenshot 18 | 19 |

Progress bar

20 | 21 | Display a progress bar. 22 | 23 | python 24 | for i in range(101): 25 | st.progress(i) 26 | do_something_slow() 27 | 28 | 29 | 30 | 31 | 32 |

screenshot

33 | 34 |

Spinner

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 |

screenshot

47 | 48 |

Status container

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 |

screenshot

61 | 62 |

Toast

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 |

screenshot

74 | 75 |

Balloons

76 | 77 | Display celebratory balloons! 78 | 79 | python 80 | st.balloons() 81 | 82 | 83 | 84 | 85 | 86 |

screenshot

87 | 88 |

Snowflakes

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 |

screenshot

105 | 106 |

Success box

107 | 108 | Display a success message. 109 | 110 | python 111 | st.success("Match found!") 112 | 113 | 114 | 115 | 116 | 117 |

screenshot

118 | 119 |

Info box

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 |

screenshot

131 | 132 |

Warning box

133 | 134 | Display warning message. 135 | 136 | python 137 | st.warning("Unable to fetch image. Skipping...") 138 | 139 | 140 | 141 | 142 | 143 |

screenshot

144 | 145 |

Error box

146 | 147 | Display error message. 148 | 149 | python 150 | st.error("We encountered an error") 151 | 152 | 153 | 154 | 155 | 156 |

screenshot

157 | 158 |

Exception output

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 |

screenshot

175 | 176 |

Stqdm

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 |

screenshot

192 | 193 |

Custom notification box

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 |

screenshot

209 | 210 |

Streamlit Extras

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 |


/content/develop/api-reference/status/balloons.md:

1 | --- 2 | title: st.balloons 3 | slug: /develop/api-reference/status/st.balloons 4 | description: st.balloons displays celebratory balloons! 5 | --- 6 | 7 | 8 |


/content/develop/api-reference/status/error.md:

1 | --- 2 | title: st.error 3 | slug: /develop/api-reference/status/st.error 4 | description: st.error displays error message. 5 | --- 6 | 7 | 8 |


/content/develop/api-reference/status/exception.md:

1 | --- 2 | title: st.exception 3 | slug: /develop/api-reference/status/st.exception 4 | description: st.exception displays an exception. 5 | --- 6 | 7 | 8 |


/content/develop/api-reference/status/info.md:

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 |


/content/develop/api-reference/status/progress.md:

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 |


/content/develop/api-reference/status/snow.md:

1 | --- 2 | title: st.snow 3 | slug: /develop/api-reference/status/st.snow 4 | description: st.snow displays celebratory snowflakes! 5 | --- 6 | 7 | 8 |


/content/develop/api-reference/status/spinner.md:

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 |


/content/develop/api-reference/status/status.md:

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 |


/content/develop/api-reference/status/success.md:

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 |


/content/develop/api-reference/status/toast.md:

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 |


/content/develop/api-reference/status/warning.md:

1 | --- 2 | title: st.warning 3 | slug: /develop/api-reference/status/st.warning 4 | description: st.warning displays warning message. 5 | --- 6 | 7 | 8 |


/content/develop/api-reference/testing/_index.md:

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 |

st.testing.v1.AppTest

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 |

AppTest.from_file

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 |

AppTest.from_string

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 |

AppTest.from_function

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 |

Block

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 |

Element

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 |

Button

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 |

ChatInput

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 |

Checkbox

184 | 185 | A representation of st.checkbox. 186 | 187 | python 188 | at.checkbox[0].check().run() 189 | 190 | 191 | 192 | 193 | 194 | 195 |

ColorPicker

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 |

DateInput

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 |

Multiselect

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 |

NumberInput

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 |

Radio

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 |

SelectSlider

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 |

Selectbox

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 |

Slider

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 |

TextArea

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 |

TextInput

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 |

TimeInput

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 |

Toggle

329 | 330 | A representation of st.toggle. 331 | 332 | python 333 | at.toggle[0].set_value("True").run() 334 | 335 | 336 | 337 | 338 | 339 |


/content/develop/api-reference/testing/st.testing.v1.AppTest.md:

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 |


/content/develop/api-reference/testing/testing_elements.md:

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 |


/content/develop/api-reference/text/_index.md:

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 | screenshot 25 | 26 |

Markdown

27 | 28 | Display string formatted as Markdown. 29 | 30 | python 31 | st.markdown("Hello **world**!") 32 | 33 | 34 | 35 | 36 | 37 |

screenshot

38 | 39 |

Title

40 | 41 | Display text in title formatting. 42 | 43 | python 44 | st.title("The app title") 45 | 46 | 47 | 48 | 49 | 50 |

screenshot

51 | 52 |

Header

53 | 54 | Display text in header formatting. 55 | 56 | python 57 | st.header("This is a header") 58 | 59 | 60 | 61 | 62 | 63 |

screenshot

64 | 65 |

Subheader

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 |

screenshot

83 | 84 |

Badge

85 | 86 | Display a small, colored badge. 87 | 88 | python 89 | st.badge("New") 90 | 91 | 92 | 93 | 94 | 95 |

screenshot

96 | 97 |

Caption

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 |

screenshot

109 | 110 |

Code block

111 | 112 | Display a code block with optional syntax highlighting. 113 | 114 | python 115 | st.code("a = 1234") 116 | 117 | 118 | 119 | 120 | 121 |

screenshot

122 | 123 |

Echo

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 |

screenshot

136 | 137 |

Preformatted text

138 | 139 | Write fixed-width and preformatted text. 140 | 141 | python 142 | st.text("Hello world") 143 | 144 | 145 | 146 | 147 | 148 |

screenshot

149 | 150 |

LaTeX

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 |

screenshot

162 | 163 |

Divider

164 | 165 | Display a horizontal rule. 166 | 167 | python 168 | st.divider() 169 | 170 | 171 | 172 | 173 | 174 | ## Utilities 175 | 176 | 177 | 178 | 179 |

Get help

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 |

Render HTML

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 |

screenshot

206 | 207 |

Annotated text

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 |

screenshot

220 | 221 |

Drawable Canvas

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 |

screenshot

234 | 235 |

Tags

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 |

screenshot

248 | 249 |

NLU

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 |

screenshot

262 | 263 |

Streamlit Extras

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 |


/content/develop/api-reference/text/badge.md:

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 |


/content/develop/api-reference/text/caption.md:

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 |


/content/develop/api-reference/text/code.md:

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 |


/content/develop/api-reference/text/divider.md:

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 |


/content/develop/api-reference/text/echo.md:

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 |


/content/develop/api-reference/text/header.md:

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 |


/content/develop/api-reference/text/help.md:

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 |


/content/develop/api-reference/text/html.md:

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 |


/content/develop/api-reference/text/latex.md:

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 |


/content/develop/api-reference/text/markdown.md:

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 |


/content/develop/api-reference/text/subheader.md:

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 |


/content/develop/api-reference/text/text.md:

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 |


/content/develop/api-reference/text/title.md:

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 |


/content/develop/api-reference/user/_index.md:

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 |

Log in a user

14 | 15 | st.login() starts an authentication flow with an identity provider. 16 | 17 | python 18 | st.login() 19 | 20 | 21 | 22 | 23 | 24 |

Log out a user

25 | 26 | st.logout() removes a user's identity information. 27 | 28 | python 29 | st.logout() 30 | 31 | 32 | 33 | 34 | 35 |

User info

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 |


/content/develop/api-reference/user/login.md:

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 |


/content/develop/api-reference/user/logout.md:

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 |


/content/develop/api-reference/user/user.md:

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 |


/content/develop/api-reference/widgets/_index.md:

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 | screenshot 16 | 17 |

Button

18 | 19 | Display a button widget. 20 | 21 | python 22 | clicked = st.button("Click me") 23 | 24 | 25 | 26 | 27 | 28 | 29 |

screenshot

30 | 31 |

Download button

32 | 33 | Display a download button widget. 34 | 35 | python 36 | st.download_button("Download file", file) 37 | 38 | 39 | 40 | 41 | 42 | 43 |

screenshot

44 | 45 |

Form button

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 |

screenshot

58 | 59 |

Link button

60 | 61 | Display a link button. 62 | 63 | python 64 | st.link_button("Go to gallery", url) 65 | 66 | 67 | 68 | 69 | 70 | 71 |

screenshot

72 | 73 |

Page link

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 |

screenshot

93 | 94 |

Checkbox

95 | 96 | Display a checkbox widget. 97 | 98 | python 99 | selected = st.checkbox("I agree") 100 | 101 | 102 | 103 | 104 | 105 |

screenshot

106 | 107 |

Color picker

108 | 109 | Display a color picker widget. 110 | 111 | python 112 | color = st.color_picker("Pick a color") 113 | 114 | 115 | 116 | 117 | 118 |

screenshot

119 | 120 |

Feedback

121 | 122 | Display a rating or sentiment button group. 123 | 124 | python 125 | st.feedback("stars") 126 | 127 | 128 | 129 | 130 | 131 |

screenshot

132 | 133 |

Multiselect

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 |

screenshot

145 | 146 |

Pills

147 | 148 | Display a pill-button selection widget. 149 | 150 | python 151 | st.pills("Tags", ["Sports", "AI", "Politics"]) 152 | 153 | 154 | 155 | 156 | 157 |

screenshot

158 | 159 |

Radio

160 | 161 | Display a radio button widget. 162 | 163 | python 164 | choice = st.radio("Pick one", ["cats", "dogs"]) 165 | 166 | 167 | 168 | 169 | 170 |

screenshot

171 | 172 |

Segmented control

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 |

screenshot

184 | 185 |

Select slider

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 |

screenshot

197 | 198 |

Selectbox

199 | 200 | Display a select widget. 201 | 202 | python 203 | choice = st.selectbox("Pick one", ["cats", "dogs"]) 204 | 205 | 206 | 207 | 208 | 209 |

screenshot

210 | 211 |

Toggle

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 |

screenshot

229 | 230 |

Number input

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 |

screenshot

242 | 243 |

Slider

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 |

screenshot

262 | 263 |

Date input

264 | 265 | Display a date input widget. 266 | 267 | python 268 | date = st.date_input("Your birthday") 269 | 270 | 271 | 272 | 273 | 274 |

screenshot

275 | 276 |

Time input

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 |

screenshot

295 | 296 |

Text input

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 |

screenshot

308 | 309 |

Text area

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 |

screenshot

321 | 322 |

Chat input

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 |

screenshot

342 | 343 |

Audio input

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 |

screenshot

355 | 356 |

Data editor

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 |

screenshot

368 | 369 |

File uploader

370 | 371 | Display a file uploader widget. 372 | 373 | python 374 | data = st.file_uploader("Upload a CSV") 375 | 376 | 377 | 378 | 379 | 380 |

screenshot

381 | 382 |

Camera input

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 |

screenshot

398 | 399 |

Streamlit Elements

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 |

screenshot

415 | 416 |

Tags

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 |

screenshot

432 | 433 |

Stqdm

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 |

screenshot

449 | 450 |

Timeline

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 |

screenshot

466 | 467 |

Camera input live

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 |

screenshot

483 | 484 |

Streamlit Ace

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 |

screenshot

500 | 501 |

Streamlit Chat

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 |

screenshot

517 | 518 |

Streamlit Option Menu

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 |

screenshot

534 | 535 |

Streamlit Extras

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 |


/content/develop/api-reference/widgets/audio_input.md:

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 |


/content/develop/api-reference/widgets/button.md:

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 |


/content/develop/api-reference/widgets/camera_input.md:

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 |


/content/develop/api-reference/widgets/checkbox.md:

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 |


/content/develop/api-reference/widgets/color_picker.md:

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 |


/content/develop/api-reference/widgets/date_input.md:

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 |


/content/develop/api-reference/widgets/download_button.md:

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 |


/content/develop/api-reference/widgets/feedback.md:

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 |


/content/develop/api-reference/widgets/file_uploader.md:

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 |


/content/develop/api-reference/widgets/link_button.md:

1 | --- 2 | title: st.link_button 3 | slug: /develop/api-reference/widgets/st.link_button 4 | --- 5 | 6 | 7 |


/content/develop/api-reference/widgets/multiselect.md:

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 |


/content/develop/api-reference/widgets/number_input.md:

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 |


/content/develop/api-reference/widgets/page_link.md:

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 |


/content/develop/api-reference/widgets/pills.md:

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 |


/content/develop/api-reference/widgets/radio.md:

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 |


/content/develop/api-reference/widgets/segmented_control.md:

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 |


/content/develop/api-reference/widgets/select_slider.md:

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 |


/content/develop/api-reference/widgets/selectbox.md:

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 |


/content/develop/api-reference/widgets/slider.md:

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 |


/content/develop/api-reference/widgets/text_area.md:

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 |


/content/develop/api-reference/widgets/text_input.md:

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 |


/content/develop/api-reference/widgets/time_input.md:

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 |


/content/develop/api-reference/widgets/toggle.md:

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 |


/content/develop/api-reference/write-magic/_index.md:

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 |

st.write

15 | 16 | Write arguments to the app. 17 | 18 | python 19 | st.write("Hello **world**!") 20 | st.write(my_data_frame) 21 | st.write(my_mpl_figure) 22 | 23 | 24 | 25 | 26 | 27 |

st.write_stream

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 |

Magic

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 |


/content/develop/api-reference/write-magic/magic.md:

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 the st.write and magic commands are and how to use them. 62 | 63 | 64 |


/content/develop/api-reference/write-magic/write.md:

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 |


/content/develop/api-reference/write-magic/write_stream.md:

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 |


/content/develop/concepts/_index.md:

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 |

Streamlit's architecture and execution model
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 |
Multipage apps
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 |
App design considerations
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 |
Connections and secrets
49 | 50 | - Learn how to manage connections and secrets with Streamlit's convenient, built-in features. 51 | 52 | 53 | 54 | 55 | 56 |
Creating custom components
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 |
Configuration and theming
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 |
App testing
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 |


/content/develop/concepts/app-design/_index.md:

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 |

Animate and update elements
13 | 14 | Understand how to create dynamic, animated content or update elements without rerunning your app. 15 | 16 | 17 | 18 | 19 | 20 |
Button behavior and examples
21 | 22 | Understand how buttons work with explanations and examples to avoid common mistakes. 23 | 24 | 25 | 26 | 27 | 28 |
Dataframes
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 |
Using custom Python classes in your Streamlit app
37 | 38 | Understand the impact of defining your own Python classes within Streamlit's rerun model. 39 | 40 | 41 | 42 | 43 | 44 |
Multithreading
45 | 46 | Understand how to use multithreading within Streamlit apps. 47 | 48 | 49 | 50 | 51 | 52 |
Working with timezones
53 | 54 | Understand how to localize time to your users. 55 | 56 | 57 | 58 | 59 |


/content/develop/concepts/app-design/animate-elements.md:

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 |


/content/develop/concepts/app-design/button-behavior-and-examples.md:

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 |


/content/develop/concepts/app-design/custom-classes.md:

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 |


/content/develop/concepts/app-design/dataframes.md:

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 |


/content/develop/concepts/app-design/multithreading.md:

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 | Each user session uses script threads to communicate between the user's front end and the Streamlit server. 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 |


/content/develop/concepts/app-design/timezone-handling.md:

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 |


/content/develop/concepts/app-testing/_index.md:

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 |


/content/develop/concepts/app-testing/automate-tests.md:

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 |


/content/develop/concepts/app-testing/beyond-the-basics.md:

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 |


/content/develop/concepts/app-testing/cheat-sheet.md:

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 |


/content/develop/concepts/app-testing/examples.md:

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 |


/content/develop/concepts/app-testing/get-started.md:

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 | A successfully completed test using pytest 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 |


/content/develop/concepts/architecture/_index.md:

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 |

Run your app
13 | 14 | Understand how to start your Streamlit app. 15 | 16 | 17 | 18 | 19 | 20 |
Streamlit's architecture
21 | 22 | Understand Streamlit's client-server architecture and related considerations. 23 | 24 | 25 | 26 | 27 | 28 |
The app chrome
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 |
Caching
37 | 38 | Make your app performant by caching results to avoid unecessary recomputation with each rerun. 39 | 40 | 41 | 42 | 43 | 44 |
Session State
45 | 46 | Manage your app's statefulness with Session State. 47 | 48 | 49 | 50 | 51 | 52 |
Forms
53 | 54 | Use forms to isolate user input and prevent unnecessary app reruns. 55 | 56 | 57 | 58 | 59 | 60 |
Widget behavior
61 | 62 | Understand how widgets work in detail. 63 | 64 | 65 | 66 | 67 |


/content/develop/concepts/architecture/app-chrome.md:

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 | App menu 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 | Settings 32 | 33 | 34 |
35 | 36 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}> 37 | Theme 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 | Record 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 | Record 66 | 67 | 68 | 6. Click "Share." 69 | 70 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}> 71 | Record 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 | Record 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 | Record 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 | Record 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 | Settings 114 | 115 | 116 | 2. Click "Deploy now." 117 | 118 | <div style={{ maxWidth: '90%', margin: '0 2em 0 2em' }}> 119 | Settings 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 | Settings 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 |


/content/develop/concepts/architecture/architecture.md:

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 |


/content/develop/concepts/architecture/caching.md:

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 | Streamlit's two caching decorators and their use cases. Use st.cache_data for anything you'd store in a database. Use st.cache_resource for anything you can't store in a database, like a connection to a database or a machine learning model. 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 |


/content/develop/concepts/architecture/forms.md:

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 | Forms 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 | Keyboard-submit forms 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('&nbsp;') 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 |


/content/develop/concepts/architecture/fragments.md:

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 | Diagram of fragment execution flow 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 |


/content/develop/concepts/architecture/run-your-app.md:

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 |


/content/develop/concepts/architecture/session-state.md:

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 | state-uninitialized-exception 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 | state-button-exception 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 | UnserializableSessionStateError 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 |


/content/develop/concepts/architecture/widget-behavior.md:

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 |


/content/develop/concepts/configuration/_index.md:

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 |

Configuration options
13 | 14 | Understand the types of options available to you through Streamlit configuration. 15 | 16 | 17 | 18 | 19 | 20 |
HTTPS support
21 | 22 | Understand how to configure SSL and TLS for your Streamlit app. 23 | 24 | 25 | 26 | 27 | 28 |
Static file serving
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 |
Theming
37 | 38 | Understand how to use the theming configuration options to customize the color and appearance of your app. 39 | 40 | 41 | 42 | 43 |


/content/develop/concepts/configuration/https.md:

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 |


/content/develop/concepts/configuration/options.md:

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 |


/content/develop/concepts/configuration/static-file-serving.md:

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("[![Click me](app/static/cat.png)](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 |


/content/develop/concepts/configuration/theming.md:

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 | Primary Color 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 | Secondary Background Color 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 |


/content/develop/concepts/connections/_index.md:

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 |

Connecting to data
13 | 14 | Connect your app to remote data or a third-party API. 15 | 16 | 17 | 18 | 19 | 20 |
Secrets managements
21 | 22 | Set up your development environement and design your app to handle secrets securely. 23 | 24 | 25 | 26 | 27 | 28 |
Authentication and user information
29 | 30 | Use an OpenID Connect provider to authenticate users and personalize your app. 31 | 32 | 33 | 34 | 35 | 36 |
Security reminders
37 | 38 | Check out a few reminders to follow best practices and avoid security mistakes. 39 | 40 | 41 | 42 | 43 |


/content/develop/concepts/connections/authentication.md:

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 |


/content/develop/concepts/connections/connecting-to-data.md:

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 |


/content/develop/concepts/connections/secrets-management.md:

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 | Secrets management FileNotFoundError 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 | Secrets management KeyError 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 |


/content/develop/concepts/connections/security-reminders.md:

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 |


/content/develop/concepts/custom-components/_index.md:

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 |


/content/develop/concepts/custom-components/components-api.md:

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 | Streamlit Component Example App 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 ArrowTables to represent dataframes. 320 |


/content/develop/concepts/custom-components/create-component.md:

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 |


/content/develop/concepts/custom-components/limitations.md:

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 |


/content/develop/concepts/custom-components/publish-component.md:

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 |


/content/develop/concepts/multipage-apps/_index.md:

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 |

Overview of multipage apps
14 | 15 | Streamlit provides multiple ways to define multipage apps. Understand the terminology and basic comparison between methods. 16 | 17 | 18 | 19 | 20 | 21 |
Define multipage apps with st.Page and st.navigation
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 |
Creating multipage apps using the pages/ directory
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 |
Working with widgets in multipage apps
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 |


/content/develop/concepts/multipage-apps/overview.md:

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 | Page not found 112 | 113 |


/content/develop/concepts/multipage-apps/page-and-navigation.md:

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 |


/content/develop/concepts/multipage-apps/page_directory.md:

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 |


/content/develop/concepts/multipage-apps/widgets.md:

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 |


/content/develop/quick-references/_index.md:

1 | --- 2 | title: Quick reference 3 | slug: /develop/quick-reference 4 | --- 5 | 6 | # Quick reference 7 | 8 | 9 | 10 | 11 | 12 |

Cheatsheet
13 | 14 | A dense list of Streamlit commands with example syntax. 15 | 16 | 17 | 18 | 19 | 20 |
Release notes
21 | 22 | See how Streamlit has changed with each new version. 23 | 24 | 25 | 26 | 27 | 28 |
Pre-release features
29 | 30 | Understand how we introduce new features and how you can get your hands on them sooner! 31 | 32 | 33 | 34 | 35 | 36 |
Roadmap
37 | 38 | Get a sneak peek at what we have scheduled for the next year. 39 | 40 | 41 | 42 | 43 |


/content/develop/quick-references/api-cheat-sheet.md:

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 |


/content/develop/quick-references/prerelease-features.md:

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 |


/content/develop/quick-references/release-notes/2019.md:

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 | - ↕️ You can now maximize charts and images! 110 | - ⚡ Streamlit is now much faster when writing data in quick succession to your app. 111 | - ✳️ Ability to blacklist folder globs from "run on save" and @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 |


/content/develop/quick-references/release-notes/2020.md:

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 | - ↕️ Ability to set the height of an 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 |


/content/develop/quick-references/release-notes/2021.md:

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 |


/content/develop/quick-references/release-notes/2022.md:

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 | - ↔️ The sidebar is now resizable via drag and drop. 110 | - ☎️ 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 | - ⚠️ Streamlit Community Cloud will support richer exception formatting. 163 | - 🏂 Get user information on private apps using 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 |


/content/develop/quick-references/release-notes/2023.md:

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 | - ↔️ st.pyplot has a 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 |


/content/develop/quick-references/release-notes/2024.md:

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 | - ↕️ The fullscreen button for charts matches with the dataframe toolbar (#9721). 82 | - 🏃 The running-man icon has a brief delay before rendering to avoid an unnecessary flicker for fast running apps (#9732). 83 | - 🖇️ The 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 | - ↕️ You can set 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 | - ↔️ You can control the initial expansion state of 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 | - ⤴️ Breaking change: We removed a patch that allows custom validators in 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment