NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #4191 most downloaded on PyPI
Create web-based user interfaces with Python. The nice way.
Last release 16 days ago
18 Sep 2026
Ships on a steady schedule
a new release about every 3 weeks
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
5 years old
315 releases · first in 2021
One column per quarter.
Revert the change from 3.17.0 that registered page('/') on a prefixed APIRouter without trailing slash, because it broke relative links like ui.naviga
page('/') on a prefixed APIRouter without trailing slash, because it broke relative links like ui.navigate.to('item') on such pages; the route is /prefix/ again as in 3.16 and as in FastAPI, and #6281 is reopened (#6346 by @KrilleGH, @falkoschindler)⚠️ Make single-use download routes of ui.download , app.add_static_file() and app.add_media_file() unguessable and truly single-use ( GHSA-p92q-2755-m
ui.download, app.add_static_file() and app.add_media_file() unguessable and truly single-use (GHSA-p92q-2755-mhgh by @hsnyus-09, @evnchn, @falkoschindler)ui.codemirror for marking ranges, styling lines, hiding or replacing text and inserting text widgets without modifying the document (#5991 by @Jepson2k, @evnchn, @falkoschindler)NICEGUI_PORT, NICEGUI_HOST or NICEGUI_PROTOCOL are set in the environment but ignored, since ui.run only uses them internally (#6301, #6337 by @evnchn, @Zeeshan-Chaudhry, @SaadZahem, @falkoschindler)button.clicked() and the initialized() awaits of ui.scene, ui.scene_view and ui.leaflet when the client is deleted instead of stranding one task per abandoned page load (#6232, #6271, #6277, #6294 by @evnchn, @SaadZahem, @falkoschindler)ui.run_javascript reporting a bogus timeout or leaking its task when the client is deleted (#6278, #6295 by @falkoschindler, @SaadZahem, @evnchn)Event.emitted() when the client is deleted (#6311, #6322 by @falkoschindler, @chiliec, @evnchn)app.handle_exception replacing the real exception with a RuntimeError after the client has been deleted, and keep running the remaining exception handlers when one of them fails (#6230, #6272, #6328 by @evnchn, @SaadZahem, @falkoschindler)ui.sub_pages and ui.select elements remaining registered after their constructor raised (#6313, #6323, #6330 by @falkoschindler, @gyanu2507, @evnchn)ui.sub_pages staying in the SPA instead of loading pages registered via app.include_router() or reached with a query string or fragment; a page('/') on a prefixed APIRouter now registers the bare prefix without trailing slash, old URLs are redirected (#6281, #6288 by @falkoschindler, @DineshThumma9, @evnchn)ui.select, ui.radio and ui.toggle rebuilding their options for every prop write during construction, which made large option lists slow to create (#6317, #6324 by @benthie, @evnchn, @falkoschindler)ui.codemirror and ui.editor losing their HTML id, which broke tooltips (#6315, #6316 by @python-and-novella, @evnchn, @falkoschindler)ui.codemirror line anchors and tooltips set together with a new value being applied to the old document, and reject line anchors on fractional lines (#6335, #6336, #6285 by @falkoschindler, @Jepson2k, @evnchn)ui.scene.move_camera() recreating the camera controls when the up vector is unchanged, which discarded user configuration and misaligned TrackballControls after a window resize (#6241, #6242, #6247 by @ptruka, @evnchn, @falkoschindler)ui.on() registers a global event after the first render, which caused flicker and lost client-side state (#6248, #6249 by @amks1, @evnchn, @falkoschindler)await_on_shutdown protection (#6263, #6329 by @evnchn, @falkoschindler)ui.popup and ui.menu not appearing in Firefox 153+ (#6221 by @SHDocter, @evnchn, @falkoschindler)ui.query's style(replace=...) replace the existing styles instead of merging them (#6182 by @evnchn, @falkoschindler)ui.code and other pre elements, which fell back to Courier on Safari (#6340, #6342 by @tcjennings, @falkoschindler, @evnchn)on_change documentation of ui.slider and ui.range, which fire while dragging and not only on release (#6291, #6292 by @KeurKeur, @evnchn, @falkoschindler)nicegui_reset_globals() not clearing Slot.stacks, which broke later User tests after an element was built outside a page (#6314, #6334 by @evnchn, @jnchen, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Breaking change: The following patterns are deprecated and will be removed in NiceGUI 4.0 (they still work but emit a one-time warning): importing fro…
ColorPicker.set_color() and ui.color_input (GHSA-955g-h32v-mvrr by @mingijunggrape, @evnchn, @falkoschindler)Refactor ui.scene to a modular object system, supporting custom three.js objects and fixing the white axes_helper regression (#4120, #6085, #6246 by @HosseyNJF, @phenolophthaleinum, @evnchn, @SaadZahem, @python-and-novella, @falkoschindler)
Breaking change: The following patterns are deprecated and will be removed in NiceGUI 4.0 (they still work but emit a one-time warning): importing from nicegui.elements.scene.scene_objects (use nicegui.elements.scene.objects instead), subclassing Object3D without a component= argument, instantiating objects with a leading type string like Object3D('group'), and the Object3D.data and Object3D.type properties.
Add line-anchor tracking to ui.codemirror, giving you stable references to specific lines that survive edits (#5988, #6282 by @Jepson2k, @evnchn, @falkoschindler)
Add h2, h3, h4, h5 and h6 HTML elements (#6238 by @SaadZahem, @evnchn, @falkoschindler)
Sync ui.table's fullscreen state back to the server when toggled from the client (#6181 by @evnchn, @falkoschindler)
Avoid downloading an unused font subset for the connection-lost popup's warning glyph (#6210 by @evnchn, @falkoschindler)
Remove dead props from ui.chip, ui.carousel_slide and the ui.date input (#6177, #6184, #6185 by @evnchn, @falkoschindler)
ui.timer's client is deleted while waiting for the connection (#6226, #6228, #6229 by @benvc, @AJ-ing, @evnchn, @falkoschindler)ui.dialogs when they are deleted (#6250 by @denniswittich, @evnchn, @falkoschindler)exact modifiers of generic events correctly and preserve hyphenated key modifiers (#6199, #6206 by @evnchn, @falkoschindler)ui.header and ui.echart and document key listeners of ui.keyboard when the element is unmounted (#6189, #6190, #6196 by @evnchn, @falkoschindler)ui.scene Object3D is deleted and guard "dragend" events against deleted objects (#6192, #6195 by @evnchn, @falkoschindler)element.move() orphaning an element when the target slot is invalid and guard against moving an element into its own descendant (#6176, #6180 by @evnchn, @falkoschindler)ui.sub_pages builders like builder errors (#6186 by @evnchn, @falkoschindler)ui.sub_pages builder raises after its client has been deleted (#6231, #6270 by @evnchn, @SaadZahem, @falkoschindler)ui.upload fails mid-transfer (#6187 by @evnchn, @falkoschindler)ui.upload unregistering the application's route instead of its own auto-generated route on delete (#6236, #6256 by @evnchn, @SaadZahem, @falkoschindler)ui.color_input crashing when the value is None with preview enabled (#6201 by @evnchn, @falkoschindler)ui.number's out_of_limits raising a TypeError when the value is None (#6179 by @evnchn, @falkoschindler)ui.line_plot's push crashing on empty data with automatic limits (#6183 by @evnchn, @falkoschindler)ui.stepper by Step object (#6200 by @evnchn, @falkoschindler)Event.emitted() timeout handling on Python 3.10 (#6197 by @evnchn, @falkoschindler)client_id forging as a commonly misreported security pattern and explain which random IDs are secrets (#6239, #6268 by @falkoschindler, @evnchn)ui.popup demo's anchor label (#6207 by @evnchn, @falkoschindler)ui.pagination's direction_links docstring (#6203 by @evnchn, @falkoschindler)User lookups and a pytest example (#6015 by @ZhuGC-Centogene, @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Breaking change: app.storage .general , .user , .tab and .client are ObservableDict s, so storage.pop('missing') now raises a KeyError instead of retu…
ui.popup, a menu on desktop and a dialog on small screens (#1125, #2149, #5613 by @DelScipio, @adosikas, @evnchn, @phifuh, @falkoschindler)run.cpu_bound process pool configurable via run.process_pool_start_method (#6117 by @evnchn, @NiklasNeugebauer, @falkoschindler)expects_arguments once at registration (#6098, #6108 by @maybites, @evnchn, @falkoschindler)Fix ObservableCollection leaking change handlers and keeping discarded containers alive (#6139, #6140 by @falkoschindler, @evnchn)
Raise KeyError from ObservableDict.pop() for a missing key, like dict.pop (#6163, #6164 by @evnchn, @falkoschindler)
Breaking change: app.storage.general, .user, .tab and .client are ObservableDicts, so storage.pop('missing') now raises a KeyError instead of returning None. Pass an explicit default to keep the previous behavior: storage.pop('missing', None).
Skip change events for no-op mutations of observable collections (#6169, #6171 by @falkoschindler, @evnchn)
Fix ui.page_title, ui.add_head_html, ui.add_body_html and ui.add_css being lost in async ui.sub_pages builders (#6058, #6147, #6148, #6168 by @cervelas, @evnchn, @falkoschindler)
Fix run.cpu_bound hanging with the forkserver start method, e.g. on Python 3.14 + Linux (#6166, #6167 by @evnchn, @falkoschindler)
Fix PermissionError on Windows when clearing storage while a backup is in flight (#6158, #6159, #6173, #6174 by @Jepson2k, @evnchn, @falkoschindler)
Fix sporadic HTTP 500 when user storage is pruned during an in-flight request (#6145, #6146 by @evnchn, @falkoschindler)
Fix TypeError in ui.input's shadow text when the autocomplete list is absent (#6161, #6162 by @Jepson2k, @evnchn, @falkoschindler)
Fix placement and order of ui.fab_actions under dir="rtl" (#6165, #6172 by @roximn148, @evnchn, @falkoschindler)
Fix crash when loading an STL model into ui.scene with wireframe=True (#5989, #6118, #6137 by @fabian0702, @Jepson2k, @evnchn, @falkoschindler)
Close streamed media file handles synchronously on teardown (#6149 by @evnchn, @falkoschindler)
examples/pytests in CI so bundled example tests can't silently rot (#6141, #6144 by @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Breaking change: Route patterns that previously failed silently (e.g. '/{_:path}' , '/{name:path}' ) now raise a ValueError at ui.sub_pages(...) / .ad…
X-Forwarded-Prefix reflection and clarify the security documentation (#6124 by @evnchn, @falkoschindler)ui.codemirror editor (#6000 by @Jepson2k, @evnchn, @falkoschindler)<html lang="..."> when ui.run(language=...) is set, and stop Quasar from forcing en-US otherwise (#5811 by @evnchn, @falkoschindler)ui.markdown.default_extras to configure Markdown extras globally (#5548, #5610 by @tomsquest, @evnchn, @falkoschindler)spool_max_size = 0 (#6102 by @falkoschindler, @evnchn)Fix character loss in ui.input (and other value elements) on high-latency connections by preserving client-originated values during element updates (#5185, #6104 by @Xitod, @samuller, @evnchn, @falkoschindler)
Fall back to polling when the WebSocket cannot connect, fixing the endless reload loop on iOS 26 Safari over plain HTTP (#5802, #6103 by @michaeljandrews, @evnchn, @falkoschindler)
Fix native window close hanging or failing to shut down the app — on Windows, after a hot reload, and when run.cpu_bound was used (#5443, #5845, #6106, #6111, #6131, #6132 by @MaidScientistIzutsumiMarin, @trivedihoney, @Mick235711, @trivedihoney, @chidoziemanagwu, @evnchn, @falkoschindler)
Fix ui.sub_pages wildcard routes not re-rendering on client-side navigation, and reject unsupported route patterns (#6090, #6092 by @JoelBender, @falkoschindler, @evnchn)
Breaking change: Route patterns that previously failed silently (e.g. '/{_:path}', '/{name:path}') now raise a ValueError at ui.sub_pages(...) / .add(...). These never matched anything before, so only already-broken routes are affected; use show_404=False together with PageArguments.remaining_path for wildcard routing instead.
Fix a selection error when changing ui.select options inside an "input-value" handler (#4420, #6100 by @platinops, @VedantMadane, @falkoschindler, @evnchn)
Fix ui.select(with_input=True) dropping its html_id, which broke tooltips (#6114, #6116 by @rolfn, @python-and-novella, @evnchn, @falkoschindler)
Fix ui.skeleton's animation_speed being sent as milliseconds instead of seconds (#6107, #6110 by @python-and-novella, @evnchn, @falkoschindler)
Fix ui.interactive_image click coordinates when a stream changes its resolution (#6122, #6123 by @LeBoozer, @evnchn, @falkoschindler)
Fix GLTF models in ui.scene being overwritten with the default material at startup (#6118, #6125 by @fabian0702, @evnchn, @falkoschindler)
Fix an infinite reload loop when an async page build exceeds response_timeout, showing a terminal error page instead (#6126, #6127 by @evnchn, @falkoschindler)
Fix the run.cpu_bound/run.io_bound return type to R | None, surfacing the latent None they already return on cancel/shutdown (#5925, #6056 by @calebgregory, @evnchn, @falkoschindler)
Note: This is a type-level change only; runtime behavior is unchanged. mypy may now flag code that typed the result as non-optional R — these are surfaced latent bugs, not regressions. NiceGUI 4.0 will raise CancelledError instead of returning None.
app.storage documentation clarity and fix inconsistencies (#5717, #5719 by @phifuh, @evnchn, @falkoschindler)NOTE: comment markers for drawing special attention (#6101 by @falkoschindler, @evnchn)push to main now that the merge queue already validates every commit reaching main, speeding up releases (#6136 by @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Introduce ui.skip_link for keyboard-accessibility skip links (WCAG 2.4.1 "Bypass Blocks") ( #5790 by @evnchn , @falkoschindler )
ui.skip_link for keyboard-accessibility skip links (WCAG 2.4.1 "Bypass Blocks") (#5790 by @evnchn, @falkoschindler)ui.codemirror via a new line_tooltips property (with optional line_tooltip_html) (#5992 by @Jepson2k, @falkoschindler, @evnchn)ui.plotly run_plot_method() helper (mirroring run_grid_method/run_chart_method) with a Plotly.extendTraces demo for appending data without re-sending the full figure (#2660, #6060 by @evnchn, @falkoschindler)app.storage, pointing at the offending key path and the underlying type instead of an internal class name (#6044, #6050 by @NichtJens, @falkoschindler, @evnchn)Fix dropped app.native.window_args, app.native.settings and app.native.start_args when the spawned window process can't re-execute the main module (e.g. console-scripts, python -m, PyInstaller); unpicklable values now emit a one-time warning instead of being silently dropped (#6082, #6093 by @VFLins, @falkoschindler, @evnchn)
Fix blank ui.aggrid when flex columns use the infinite row model; auto_size_columns becomes a tri-state bool | None defaulting to None (skip autoSizeStrategy when columns use flex) (#5087, #6080 by @davetapley, @evnchn, @falkoschindler)
Fix Event.emitted() returning None instead of the emitted arguments, adding an opt-in expect_args parameter to Event.subscribe() (#6077, #6078 by @falkoschindler, @evnchn)
Fix the ui.sub_pages 404 sad-face persisting after client-side navigation when the page is first loaded at an unknown URL (#6069, #6071 by @evnchn, @falkoschindler)
Note: Apps that relied on the styled sad-face overlay appearing on ui.sub_pages 404s now see only the inline 404: sub page X not found label inside their own chrome. The HTTP 404 status and the show_404=False escape hatch are unchanged.
Replace the misleading "remove the main guard" reload error for python -m <pkg> with a targeted message naming the package and the working alternatives (#181, #3769, #5280, #5939, #6066, #6068 by @NichtJens, @evnchn, @falkoschindler)
Fix Client has been deleted warning spam on reload when scene objects are mutated after await (#6058, #6059 by @codingpaula, @evnchn, @falkoschindler)
Note: Element.update(), run_method() and get_computed_prop() now stay silent only for the disconnect/reload race; using an element after an explicit element.delete() emits a one-shot warning with a stack trace.
Accept, emit heading prefixes while skipping decorative HTML and demo previews, and add aria-label/child-content labels to button Markdown (#6007, #6052, #6053, #6054 by @evnchn, @falkoschindler, @HosseyNJF)llms.md gaps in the element surface vs. ui.* exports (#6048, #6051 by @evnchn, @falkoschindler)ui.plotly responsive with large datasets by passing NumPy arrays / pandas Series instead of Python lists (#3340, #6095 by @TsaiTung-Chen, @python-and-novella, @mohsenminaei, @krashdifferent, @eddie3ruff, @buiapp, @falkoschindler, @evnchn)Storage.path regression (#5960, #6061, #6075 by @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix auto-prune timers being skipped in script mode ( #6072 , #6074 by @evnchn , @falkoschindler )
ui.upload routes listed in /docs and /openapi.json (#6063 by @jsb-zz, @falkoschindler, @evnchn)ui.aggrid events (#2878, #6067 by @BlankAdventure, @KrilleGH, @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
⚠️ Prevent local file disclosure in ui.restructured_text via Docutils file insertion directives ( GHSA-jfrm-rx66-g536 by @dennyabrahamsinaga , @h3ri0s
ui.restructured_text via Docutils file insertion directives (GHSA-jfrm-rx66-g536 by @dennyabrahamsinaga, @h3ri0s, @falkoschindler, @evnchn)enable(), disable(), set_enabled(), set_visibility() and many more methods chainable (#6014, #6016 by @stragrothsk, @falkoschindler, @evnchn)ui.mermaid content like ui.markdown does (#6011, #6012 by @BasementSociety, @falkoschindler, @evnchn)KeyboardInterrupt traceback from run.cpu_bound workers on Ctrl-C (#6025, #6027 by @justus2510, @falkoschindler, @evnchn)RuntimeError: A SemLock created in a fork context is being shared with a process in a spawn context in ui.run(native=True, reload=True) on CPython ≥ 3.11.5 (#1841, #6045 by @swrpug, @evnchn, @falkoschindler, @rodja, @JensOgorek, @yuanxion, @electronstudio, @knoppmyth, @Qhawaq, @sagarbehere, @frankhuurman, @MrGibus, @BanDroid)KeyError: 'error' when calling ValidationElement.validate() after the error prop was removed via props(remove=...) (#5977, #6042 by @manu-ns, @falkoschindler, @evnchn)window.location.reload() on every WebSocket reconnect (#6018, #6019 by @arodidev, @falkoschindler, @evnchn)ElementFilter.DEFAULT_LOCAL_SCOPE at runtime so changing the class variable actually affects new instances (#6005, #6013 by @gzu300, @DarkRiddle1212, @falkoschindler)app.timer and lifecycle handler (on_connect, on_disconnect, on_delete, on_shutdown, on_exception) registration in script mode (#6003, #6006 by @EchterTimo, @falkoschindler, @evnchn)ui.aggrid.from_pandas and ui.table.from_pandas (#5995, #6002 by @NichtJens, @DarkRiddle1212, @falkoschindler, @evnchn)df.reset_index(drop=True) before passing the DataFrame.ui.code reporting the wrong language and throwing a ReferenceError in the CodeMirror findLanguage error path (#5982 by @Jepson2k, @falkoschindler)ui.scene (#3515, #5708 by @maria-korosteleva, @evnchn, @falkoschindler, @fabian0702)llms.md — a self-contained LLM reference covering NiceGUI's API surface, mental models, and common anti-patterns for AI-assisted development (#6021, #6049 by @joko-zauberzeug, @falkoschindler, @evnchn, @rodja)Sortable documentation (#6017 by @falkoschindler, @denniswittich)client_id security model and add an "Examples Are Starting Points" callout (#6004 by @evnchn, @falkoschindler)ui.sub_pages doc demo link target and label (#5999, #6008 by @aisartag, @falkoschindler, @evnchn)ui.checkbox and ui.switch now receive e.args = None instead of not element.value; read e.sender.value (or use on_value_change) for the post-toggle value.find(...).click() no longer broadcasts to every matched element — it picks the lowest-ID enabled match and dispatches once. Tests that relied on simultaneous multi-match clicks need to issue separate calls.ui.sub_pages navigation in user-simulated tests (#5193 by @rodja, @falkoschindler)uv sync --locked in CI workflows so lockfile drift fails fast with a clear diagnostic (#6036 by @evnchn, @falkoschindler)uv sync --locked in Copilot setup steps for deterministic agent boot environments (#6040 by @evnchn, @falkoschindler)pip ecosystem to uv (#6037 by @evnchn, @falkoschindler)[project.urls] with PEP 753 well-known labels (#6039 by @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix ui.sub_pages subpaths returning a JSON 404 instead of rendering the page when used inside ui.run_with(root=...) ( #5998 , #5999 by @egursu , @falk
ui.sub_pages subpaths returning a JSON 404 instead of rendering the page when used inside ui.run_with(root=...) (#5998, #5999 by @egursu, @falkoschindler, @evnchn)Add lazy imports for the ui module to speed up startup by deferring heavy element imports until first access ( #5303 by @evnchn , @falkoschindler )
ui module to speed up startup by deferring heavy element imports until first access (#5303 by @evnchn, @falkoschindler)ValueElement and ValueChangeEventArguments generic for proper type inference of .value and event arguments (#2677, #5785 by @petergaultney, @evnchn, @falkoschindler)make_sortable() to enable drag-and-drop sorting on container elements like ui.column, ui.row, ui.card, and many more (#932, #4656, #5464, #5855 by @falkoschindler, @evnchn, @EmberLightVFX, @phifuh, @marcrichard22, @nomadfox6, @HansBambel, @sergeyyurkov1, @sukhstanford, @stepheweffie, @TM0088, @weihuan)Accept: text/markdown content negotiation for AI agents and CLI tools (opt-in via ui.run(markdown=True) or @ui.page(markdown=True)) (#5889 by @evnchn, @falkoschindler, @rodja)ui.run_with() is called with nicegui.app itself, preventing a self-mount loop that exhausted CPU and RAM (#5954 by @falkoschindler, @evnchn)ui.keep_alive to mount children eagerly regardless of visibility, fixing data loss in unmounted ui.xterm and unresponsive ui.aggrid inside hidden tabs or dialogs (#3033, #5839, #5951 by @falkoschindler, @natankeddem, @kyloe, @amks1, @Ha44a, @evnchn)dragover handler (#5943, #5962 by @phifuh, @falkoschindler, @evnchn)app.on_exception not catching exceptions from async event handlers (#5945, #5946 by @Rollmops, @falkoschindler, @evnchn)PermissionError on Windows when clearing file-backed storage during shutdown (#5949, #5973 by @atollk, @rodja, @falkoschindler, @evnchn)@keyframes and similar nested at-rules, and apply scoped selectors to the component root element via tinycss2 (#5969, #5972, #5976 by @jhjcpishva, @falkoschindler, @evnchn)ui.interactive_image when size is not set, fixing stretched sponsor logos on the website (#5978, #5980 by @lawrenceakka, @falkoschindler, @evnchn)ReferenceError from dangling weakref proxies in ui.run_with() (#5979, #5981 by @giunio-prc, @falkoschindler, @evnchn)ui.aggrid demo for adding rows without losing client-side edits (#5952, #5967 by @peyman-vasegh, @falkoschindler, @evnchn)RuntimeError when a click handler deletes the clicked element during testing (#5935, #5937, #5938 by @samuller, @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
⚠️ Prevent filename sanitization bypass via ui.upload on Windows ( GHSA-w8wv-vfpc-hw2w by @offset , @evnchn , @falkoschindler )
ui.upload on Windows (GHSA-w8wv-vfpc-hw2w by @offset, @evnchn, @falkoschindler)ui.status_code() to set HTTP status codes from page builders (#5810 by @evnchn, @falkoschindler)on_resize callback to ui.xterm to communicate terminal size to the PTY (#5847, #5858 by @jacopofar, @evnchn, @falkoschindler)ui.tab to find ui.tabs ancestor through intermediate containers (#5902 by @falkoschindler, @evnchn)helpers.py into a package with focused submodules (#5916 by @falkoschindler, @evnchn)client.ip always reporting "127.0.0.1" behind reverse proxies and On Air (#4786, #5906, #5920 by @ffilotto, @evnchn, @falkoschindler, @stephanpalmer)ValidationElement.error being ignored when no validation is set (#5895, #5903 by @atollk, @falkoschindler, @evnchn)tailwind=False (#5868, #5904 by @platinops, @evnchn, @falkoschindler)initialized() adding event listeners on every call (#5898 by @falkoschindler, @evnchn)ui.timer callback tasks leaking when client disconnects mid-execution (#5930, #5931 by @NiklasNeugebauer, @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix Dependabot alerts by upgrading vulnerable npm and pip dependencies (#5850 by @falkoschindler, @evnchn)
ui.parallax element based on Quasar Parallax (#4817 by @evnchn, @falkoschindler)ui.scene (#3710, #4820 by @javierlopezrodriguez, @evnchn, @falkoschindler)app.native (#3378, #5866 by @EmberLightVFX, @falkoschindler, @evnchn)app.clients() to return all clients when path is None (#5853 by @AlePiccin, @evnchn, @falkoschindler)KWONLY_SLOTS constant and inline @dataclass arguments (#5856 by @falkoschindler, @evnchn)SessionMiddleware (#2578, #5857 by @denniswittich, @falkoschindler, @evnchn)ui.log scroll to bottom on Firefox (#5788, #5842 by @platinops, @falkoschindler, @evnchn)@ui.page routes (#5705, #5777, #5886 by @CatamountJack, @falkoschindler, @evnchn)ui.code by always using DOMPurify (#5877 by @falkoschindler, @evnchn)find_spec crashes in compiled environments like PyInstaller (#5747, #5836 by @evnchn, @falkoschindler)@ui.refreshable_method refresh only updating last instance (#5888, #5890 by @rodja, @evnchn)app.storage.individual API with app.storage.user (#5874 by @KrilleGH)User.should_see for child elements inside hidden containers (#5873 by @paco-sevilla, @falkoschindler)User test simulation for select options with None as value (#5880, #5883 by @atollk, @falkoschindler, @evnchn)ui.tab in user simulation (#5885, #5887 by @atollk, @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Breaking change: For security reasons, run_method() and run_*_method() no longer accept arbitrary JavaScript expressions as method names. Only actual…
⚠️ Prevent XSS via unsanitized method names in run_method() (GHSA-78qv-3mpx-9cqq by @anuraagbaishya, @evnchn, @falkoschindler)
Breaking change: For security reasons, run_method() and run_*_method() no longer accept arbitrary JavaScript expressions as method names. Only actual method names are supported now. If you previously passed JS functions like
row = await grid.run_grid_method('(g) => g.getDisplayedRowAtIndex(0).data')
use
row = await run_javascript(f'return getElement({grid.id}).api.getDisplayedRowAtIndex(0).data')
instead.
ui.codemirror.set_value (#5775 by @falkoschindler, @evnchn)stop() to avoid ~1s shutdown delay (#5804, #5805 by @Denubis, @evnchn, @falkoschindler)prune_user_storage crash when UI elements are created before ui.run_with() (#5480, #5768 by @TulyOpt, @Phloog, @jammerhund, @evnchn, @falkoschindler)ui.aggrid.VERSION constant for AG Grid version reference (#5726, #5727 by @taschini, @evnchn, @falkoschindler)ui.html, ui.markdown and ui.interactive_image to avoid server-side updates overwriting client-side DOM modifications (#5749, #5761, #5816, #5821, #5823, #5826 by @Denubis, @phifuh, @evnchn, @falkoschindler)ui.echart zoom reset on data update by using getOption() API (#5819, #5822 by @rtrrtr, @falkoschindler, @evnchn)ui.log background color being tinted by inner scroll-area element (#5828, #5831 by @rolfn, @falkoschindler, @evnchn)ui.table fullscreen toggle with smooth scrolling enabled (#5789 by @falkoschindler, @evnchn)beforeUnmount hooks during @ui.refreshable rebuild (#5765, #5766 by @evnchn, @falkoschindler)h-screen (#5792 by @evnchn)UserInteraction (#5771, #5772 by @atollk, @falkoschindler, @evnchn)_page_exception_handler in App.reset() for test isolation (#5809 by @evnchn)nanasess/setup-chromedriver from CI in favor of runner's preinstalled Chrome (#5740 by @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix .mjs files served with wrong MIME type on some Windows machines (#5723, #5724 by @python-and-novella, @evnchn)
Update dependencies to fix security vulnerabilities (#5695 by @falkoschindler, @evnchn)
ui.markdown() (GHSA-v82v-c5x8-w282 by @falkoschindler, @evnchn)FileUpload.name enabling arbitrary file write (GHSA-9ffm-fxg3-xrhh by @k14uz, @evnchn, @falkoschindler)on_connect called before page is ready (#5673 by @evnchn, @falkoschindler)checkboxRenderer in favor of built-in agCheckboxCellRenderer (#5681, #5685 by @CatamountJack, @CrystalWindSnake, @falkoschindler, @evnchn)ui.interactive_image with PIL images (#5583, #5653 by @denniswittich, @evnchn, @falkoschindler)app.shutdown for ui.run_with (#3253, #5686 by @MuuXB, @python-and-novella, @falkoschindler, @evnchn, @falkoschindler)NiceGUIJSONResponse to inherit from JSONResponse so response model schema appears in Swagger (#5688, #5689 by @AleDetto, @evnchn, @falkoschindler)ui.expansion stutters during animation (#4918, #5659 by @platinops, @evnchn, @falkoschindler)ui.dialog (#2149, #5652 by @adosikas, @meslahik, @python-and-novella, @liunux4odoo, @evnchn, @falkoschindler)tab_id in User fixture (#5687, #5690 by @5553455237, @evnchn, @falkoschindler)uv.lock to revision 3 (#5707 by @evnchn, @falkoschindler)contextlib.suppress to ignore exceptions (#5714 by @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix hot reload and On Air connection by reverting PR #5499 (#5671 by @python-and-novella, @falkoschindler)
value when closing a ui.select popup in User simulation (#4894, #5670 by @briemla, @falkoschindler, @evnchn, @rodja)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Introduce `app.colors` for global color configuration (#3787, #5611 by @ghbm-itk, @evnchn, @falkoschindler)
app.colors for global color configuration (#3787, #5611 by @ghbm-itk, @evnchn, @falkoschindler)set_background_color() and bind_text_color() (#5511, #5512 by @borolepratik, @evnchn, @falkoschindler)ui.on_exception for handling exceptions after the page has been sent to the client (#5617, #5618 by @CatamountJack, @evnchn, @falkoschindler)ui.altair accept any Altair chart type like LayerChart or FacetChart (#5649, #5650 by @s-meza, @falkoschindler)ui.run(show=...) to open a specific page (#5642 by @jsb-zz, @falkoschindler, @evnchn)ui.tabs and ui.tab_panels always emit change values as string (#5637, #5638 by @jeffective, @evnchn, @falkoschindler)ui.scene loses the WebGL context (#5360, #5656 by @falkoschindler, @evnchn)ui.add_css after client connected (#5624, #5628, #5666, #5667 by @svhb1000, @falkoschindler, @evnchn)ui.dialog being hidden when created inside ui.menu (#4116, #5602 by @tgbl-mk, @evnchn, @falkoschindler)ui.leaflet tiles not loading when element is unhidden (#2338, #5614 by @kleynjan, @evnchn, @falkoschindler)ui.log scroll-to-bottom in ui.tab_panel (#5118, #5632 by @pandabearcodes, @evnchn, @falkoschindler)ui.anywidget not propagating back to frontend (#5626, #5636 by @s-meza, @evnchn, @falkoschindler)on_path_changed demo and reference for SubPagesRouter (#5487, #5665 by @GinjiJizai, @falkoschindler, @evnchn)ui.context.client.connected (#5607 by @jeffective, @falkoschindler, @evnchn)asyncio_default_fixture_loop_scope setting (#4785 by @gzu300, @evnchn, @rodja, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
⚠️ Prevent Zero-click XSS attacks via user-defined link fragments (GHSA-mhpg-c27v-6mxr by @evnchn, @falkoschindler)
ui.sub_pages (GHSA-m7j5-rq9j-6jj9 by @evnchn, @falkoschindler, @xx-mikusan-xx)ui.navigate.push and ui.navigate.replace (GHSA-7grm-h62g-5m97 by @xx-mikusan-xx, @evnchn, @falkoschindler)ui.anywidget and ui.altair (#657, #5096, #5137 by @murilomm192, @Jerold-S, @azjps, @evnchn, @falkoschindler, @Noghpu, @s-meza)on_click for ui.echart (#5576, #5578 by @TheOtherRealm, @evnchn, @falkoschindler)ui.json_editor (#4748, #5571 by @phnmn, @evnchn)ui.plotly and ui.joystick into JavaScript components for faster loading (#5567, #5568 by @evnchn, @falkoschindler)prefix and suffix properties to ui.input and ui.number (#5534 by @Yuerchu, @evnchn, @falkoschindler)ui.mermaid sending error events to wrong UI element (#5597, #5599 by @AwMalka, @evnchn, @falkoschindler)ui.timer leaking memory when client disconnects immediately (#5595, #5598 by @ftilde, @evnchn, @falkoschindler).vue components (#5619 by @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Load Vue faster (#5560 by @evnchn)
root function with ui.sub_pages (#5579, #5580 by @codingpaula, @falkoschindler, @evnchn)ui.echart (#5576 by @TheOtherRealm, @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
⚠️ Deprecate `ui.add_scss` and `ui.add_sass` in favor of `ui.add_css` (#5321, #5449 by @falkoschindler, @evnchn)
app.add_media_files (GHSA-hxp3-63hc-5366 by @y4rvin, @evnchn, @falkoschindler)ui.add_css, ui.add_scss, or ui.add_sass (GHSA-72qc-wxch-74mg by @twmoon, @evnchn, @falkoschindler)ui.interactive_image using a new sanitize parameter (GHSA-2m4f-cg75-76w2 by @twmoon, @evnchn, @falkoschindler)element.clear() return the element (builder pattern) (#5461 by @falkoschindler, @evnchn)ui.sub_pages (#5437, #5440, #5529, #5530, #5541 by @davetapley, @evnchn, @falkoschindler, @rodja)shared=True in ui.add_head_html (#5472, #5478 by @davetapley, @evnchn, @falkoschindler)--clean and --noconfirm to pyinstaller in nicegui-pack (#5469 by @himbeles)ui.add_scss and ui.add_sass in favor of ui.add_css (#5321, #5449 by @falkoschindler, @evnchn).tooltip() for complex elements like ui.table (#5447, #5482 by @pandabearcodes, @python-and-novella, @evnchn, @himbeles, @falkoschindler)ui.interactive_image (#5479, #5517 by @daniel-anderberg, @evnchn, @falkoschindler)ui.range (#5467 by @Mick235711)ui.stepper (#5434, #5463 by @Buruxianian, @evnchn, @falkoschindler)ui.sub_pages demo (#5498, #5504 by @evnchn, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Do not load headwind.css if Tailwind is disabled (#5448 by @evnchn)
headwind.css if Tailwind is disabled (#5448 by @evnchn)ui.plotly not being initialized (#5459, #5460 by @mathisschmidt, @falkoschindler, @evnchn)ui.table "found list in column" warnings (#5454 by @Mick235711, @falkoschindler)ui.leaflet when calling set_center and set_zoom in combination (#4491, #5453 by @amks1, @falkoschindler, @evnchn)ui.sub_pages in root page with remaining path (#5442 by @rodja)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Introduce `ui.date_input` and `ui.time_input` (#4815 by @evnchn, @falkoschindler)
ui.date_input and ui.time_input (#4815 by @evnchn, @falkoschindler)ui.page_scroller (#4816 by @evnchn, @falkoschindler)on_node_click using a no-markdown solution (#4845, #4862, #4871 by @thetableman, @evnchn, @falkoschindler)run.cpu_bound fails in script mode (#5382 by @wschoenell, @evnchn, @rodja, @falkoschindler)ui.plotly (#4870, #5369, #5397, #5404 by @lucasimi, @AlePiccin, @evnchn, @falkoschindler)ui.markdown containing Mermaid diagrams (#5366 by @falkoschindler, @evnchn)VBuild (#5393 by @falkoschindler, @evnchn)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Allow awaiting refreshable function updates for coordinated UI operations (#4985, #5358 by @MaidScientistIzutsumiMarin, @gtato, @rodja, @falkoschindle
fps and show_stats parameters for ui.scene (#5344, #5349 by @evnchn, @falkoschindler)ui.codemirror (#5341, #5342 by @ZeroPoint095, @falkoschindler)ui.table rows (#5355 by @steweg, @evnchn, @falkoschindler)ui.table cells contain lists (#4837, #5290 by @WilliamAughenbaugh, @samuller, @evnchn, @falkoschindler)terminal when mouinting ui.xterm (#5343, #5350 by @falkoschindler, @evnchn)ui.xterm subprocess demo (#5353, #5362 by @jayjensen, @evnchn, @paco-sevilla, @falkoschindler)trigger() in User tests (#5339 by @rodja, @falkoschindler, @evnchn)Screen.CATCH_JS_ERRORS to opt-out from JavaScript errors (#5365 by @falkoschindler, @evnchn)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Run Uvicorn with ws='wsproto' to avoid using a deprecated protocol (#5256 by @evnchn)
ui.xterm (#4520 by @paco-sevilla, @evnchn, @falkoschindler)request parameter (#5263 by @rodja)ui.sub_pages and ui.context.client.sub_pages_router (#5192 by @rodja)ui.run() (#4252, #5213 by @AlyShmahell, @rodja, @samuller, @falkoschindler)--onedir argument to nicegui-pack (#5091, #5114 by @djongepier)ws='wsproto' to avoid using a deprecated protocol (#5256 by @evnchn)ui.aggrid (#5293, #5306 by @gioxc88, @python-and-novella, @gioxc, @evnchn, @AMorelProjects, @falkoschindler)ui.aggrid (#5244, #5276, #5277, #5296 by @amks1, @guyingbo, @falkoschindler, @PhilippMolitor, @evnchn, @gioxc88)ui.code after content has been changed (#5177, #5298 by @jlmanaloto, @python-and-novella, @falkoschindler, @evnchn)ui.code without language specification (#5264, #5266 by @rolfn, @evnchn, @falkoschindler)ui.leaflet not being fully functional when draw_control is False (#5279, #5281, #5317, #5319 by @lisa-ems, @evnchn, @falkoschindler, @jacksonhshields)ui.separator in vertical orientation (#5270, #5271 by @backbord, @python-and-novella, @evnchn, @falkoschindler)update methods (#5267 by @helmut-hoffer-von-ankershoffen, @evnchn, @python-and-novella, @falkoschindler)user_plugin in demo to avoid Selenium dependency (#5262 by @rodja, @BayerSe)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix exception handling when emitting or calling events (#5250 by @falkoschindler)
ui.table rows (#5150 by @Mogit21, @evnchn, @falkoschindler)Fix bugs related to the new `Event` system (#5216, #5221 by @v479038280, @python-and-novella, @falkoschindler)
Event system (#5216, #5221 by @v479038280, @python-and-novella, @falkoschindler)
ui.notify documentation link (#5212 by @samuller)This release fixes the Docker image which failed to build for version 3.0.1.
This release fixes the Docker image which failed to build for version 3.0.1.
Respect show_404=False in `ui.sub_pages` navigation (#5199 by @rodja, @falkoschindler)
show_404=False in ui.sub_pages navigation (#5199 by @rodja, @falkoschindler)user.should_see work with icon and chat message names (#5183, #5184 by @phifuh, @rodja)This major release introduces several new features and enhancements, as well as breaking changes. We always try to keep breaking changes to a minimum,…
This major release introduces several new features and enhancements, as well as breaking changes. We always try to keep breaking changes to a minimum, guide you through the migration process using deprecation warnings, and provide migration instructions. Please read the following release notes carefully to understand the changes and adapt your code accordingly before upgrading.
root page parameter for ui.run to simplify single-page applications (#4964, #5005 by @rodja, @falkoschindler)Event system to communicate between UI and long-living objects (#5107 by @falkoschindler, @denniswittich)esm Parameter for custom elements (#4163, #5021 by @simontaurus, @evnchn, @falkoschindler)ValueChangeEventArguments.previous_value (#4410, #4456 by @me21, @falkoschindler, @evnchn)strict option for binding methods (#5040 by @denniswittich, @codingpaula, @falkoschindler)sanitize argument for ui.html and ui.chat_message (#5187 by @oxqnd, @falkoschindler, @rodja, @evnchn)q-scroll_area for ui.log to preserve a fixed height (#4925, #4935 by @e-nesse, @falkoschindler)ui.clipboard.read() return None if the clipboard API isn't available (#4236, #4237 by @weinibuliu, @falkoschindler, @evnchn)UploadEventArguments and MultiUploadEventArguments to avoid unclosed resources (#5178 by @rodja, @evnchn, @falkoschindler)run.cpu_bound (#4980, #5129 by @jammerhund, @evnchn, @rodja, @falkoschindler)Shared auto-index pages (not using @ui.page)
UI elements defined in global scope have been added to a static shared "auto-index client" served at "/". This caused a multitude of problems throughout the code base, so we decided to remove this shared client.
In version 3.0 you have the following options:
Keep putting (all) UI elements in global scope. We call such apps without page functions "NiceGUI scripts". They will automatically re-evaluated inside an implicit page function when visiting "/". This is almost a drop-in replacement for the auto-index client, but:
Wrap all UI in a singe function and pass it to the new positional root parameter in ui.run. This is especially handy in combination with ui.sub_pages. This way you can create rich single-page applications without worrying about defining the correct routes with @page decorators.
Use page functions for all your pages, including the index page at "/".
Note that we are introducing a new Event class. Together with the binding module this helps to synchronize long-living objects with short-living UI without relying on a long-living shared client.
Calling .props(), .classes() or .style() with subsequent .update()
A subsequent .update() is not necessary anymore because props, classes and style are now observable collections that trigger updates automatically.
When overwriting the update method in custom elements, infinite recursions can occur. If, e.g., the update methods uses .prop() before calling super().update(), the .prop() call will cause an infinite cycle. Wrap it with with self._props.suspend_updates(): (and similar for classes and style) to pause automatic updates in such cases.
Modifying the content of ui.table, ui.aggrid, ui.echart, ui.highchart, ui.json_editor, and ui.tree
Before NiceGUI 3.0 it was possible create tables or AG Grids like ui.table(columns=columns, rows=rows, ...) or ui.aggrid({'columnDefs': columns, 'rowData': rows, ...) and update it later by modifying the original columns or rows. This is not possible anymore.
In NiceGUI 3.0 you need to modify table.rows, table.columns or aggrid.options instead. A subsequent table.update() or aggrid.update is not necessary anymore.
Same holds for ui.echart, ui.highchart, ui.json_editor, and ui.tree.
Upgrade to Tailwind 4; dropping the ui.element.tailwind API
Although very similar, Tailwind 4 comes with some breaking changes. Check your layout carefully after upgrading. We noticed differences especially with line spacing and borders.
Custom style definitions of Quasar elements might need to be adjusted. Because all Tailwind and Quasar CSS has moved into layers, custom overrides need to move as well. See #5240 for more information.
For technical reasons updating and maintaining our ui.element.tailwind API became unfeasible. So we decided to remove it. For auto-completing Tailwind classes, we recommend the NiceGUI extension for Visual Studio Code by @DaelonSuzuka.
Dropping support for Python 3.8
Almost one year after Python 3.8 reached its end-of-life, it was time to drop support. This allowed us to update the code to a newer standard and resolve some issues with Python dependencies that already dropped 3.8 a while ago.
ValueChangeEventArguments got a new previous_value attribute
In some situations it might be helpful to have access to both, the current and the previous value. Therefore we added previous_value to the ValueChangeEventArguments.
Custom elements that emit ValueChangeEventArguments need to provide the previous value.
Binding from and to non-existing object properties
Binding used to fail silently if one of the attributes doesn't exist. In case of dictionaries this was intended, because they are often used to bind to persistent storage which is empty by default. But for object properties this can lead to very subtle bugs, e.g. after renaming properties and not updating the attribue names in binding functions.
In 3.0 object properties will be checked for existence by default. Missing dictionaries will continue to be ignored.
You can fine-tune this behavior using the strict: bool | None = None parameter (None: check object properties and ignore dictionaries).
Sanitize content for ui.html and ui.chat_message
To prevent NiceGUI apps from accidentally displaying unsafe user input as HTML, ui.html and ui.chat_message now have a sanitize argument. It can be set to a function like Sanitizer().sanitize from the html-sanitizer module, or False to disable sanitization. While ui.html requires the new argument to be specified, ui.chat_message only requires it if text_html=True.
Note that the html-sanitizer module is not included in NiceGUI and needs to be installed separately is needed.
ui.log with unspecified width can collapse inside containers with unspecified width
We noticed that the height of ui.log could be affected by its content, which is unexpected. Therefore we decided to use a scroll area for a more robust layout.
Now the width of ui.log can collapse when placed inside a container with an unspecified width. Either give the container some width or specify the width of ui.log.
ui.clipboard.read() returns None if the clipboard API isn't available
The read() function used to return an empty string if the clipboard API is not available. This was indistinguishable from an empty clipboard.
Now the read() function returns None if the clipboard API is not available.
Lifecycle events are called more consistently
Handlers registered with on_disconnect used to be called only after the client fails to reconnect and is deleted.
Now disconnect handlers are immediatelly called when a connection is lost, even if the client reconnects after a short while.
A new on_delete function can be used to register callbacks for the deletion of a client.
UploadEventArguments and MultiUploadEventArguments have changed
Instead of content: BinaryIO, name: str and type: str the event arguments now contain a single field file: FileUpload, which contains
name: str and content_type: str as well asread, text, json, iterate, save and size to access the content.Remove deprecated code and APIs
app.add_static_file and app.add_media_file raises FileNotFoundError instead of ValueError in case of non-existing files. Since FileNotFoundError is NOT a subclass of ValueError, existing code which except ValueError will fail to catch the new FileNotFoundError.ui.aggrid: run_column_method is gone. Use run_grid_method instead.ui.table calling add_rows()/remove_rows() with variable-length arguments no longer works. Pass a list instead or use add_row()/remove_row() for a single row.ui.open is gone. Use ui.navigate.to.nicegui.testing.conftest is gone and you can no longer import it. Use pytest_plugins = ["nicegui.testing.plugin"] instead.element.on parameter js_handler now has type str instead of str | None. You can pass a None and it still works, but your type checker won't be happy, and we can't promise it will work for much longer afterwards.The following JavaScript dependencies have been updated to the latest versions (#5034, #5168, #5190 by @falkoschindler):
The following contributions helped to fix problems during integration:
ui.aggrid (#5122, #5131 by @egursu, @evnchn, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
We always try to keep breaking changes to a minimum, guide you through the migration process using deprecation warnings, and provide migration instruc…
root page parameter for ui.run to simplify single-page applications (#4964, #5005 by @rodja, @falkoschindler)Event system to communicate between UI and long-living objects (#5107 by @falkoschindler, @denniswittich)ValueChangeEventArguments.previous_value (#4410, #4456 by @me21, @falkoschindler, @evnchn)strict option for binding methods (#5040 by @denniswittich, @codingpaula, @falkoschindler)q-scroll_area for ui.log to preserve a fixed height (#4925, #4935 by @e-nesse, @falkoschindler)ui.clipboard.read() return None if the clipboard API isn't available (#4236, #4237 by @weinibuliu, @falkoschindler, @evnchn)We always try to keep breaking changes to a minimum, guide you through the migration process using deprecation warnings, and provide migration instructions. Please read the following release notes carefully to understand the changes and adapt your code accordingly before upgrading.
Shared auto-index pages (not using @ui.page)
UI elements defined in global scope have been added to a static shared "auto-index client" served at "/". This caused a multitude of problems throughout the code base, so we decided to remove this shared client.
In version 3.0 you have the following options:
Keep putting (all) UI elements in global scope. We call such apps without page functions "NiceGUI scripts". They will automatically re-evaluated inside an implicit page function when visiting "/". This is almost a drop-in replacement for the auto-index client, but:
Wrap all UI in a singe function and pass it to the new positional root parameter in ui.run. This is especially handy in combination with ui.sub_pages. This way you can create rich single-page applications without worrying about defining the correct routes with @page decorators.
Use page functions for all your pages, including the index page at "/".
Note that we are introducing a new Event class (see below). Together with the binding module this helps to synchronize long-living objects with short-living UI without relying on a long-living shared client.
Calling .props(), .classes() or .style() with subsequent .update()
A subsequent .update() is not necessary anymore because props, classes and style are now observable collections that trigger updates automatically.
When overwriting the update method in custom elements, infinite recursions can occur. If, e.g., the update methods uses .prop() before calling super().update(), the .prop() call will cause an infinite cycle. Wrap it with with self._props.suspend_updates(): (and similar for classes and style) to pause automatic updates in such cases.
Upgrade to Tailwind 4; dropping the ui.element.tailwind API
Although very similar, Tailwind 4 comes with some breaking changes. Check your layout carefully after upgrading. We noticed differences especially with line spacing and borders.
For technical reasons updating and maintaining our ui.element.tailwind API became unfeasible. So we decided to remove it. For auto-completing Tailwind classes, we recommend the NiceGUI extension for Visual Studio Code by @DaelonSuzuka.
Dropping support for Python 3.8
Almost one year after Python 3.8 reached its end-of-life, it was time to drop support. This allowed us to update the code to a newer standard and resolve some issues with Python dependencies that already dropped 3.8 a while ago.
ValueChangeEventArguments got a new previous_value attribute
In some situations it might be helpful to have access to both, the current and the previous value. Therefore we added previous_value to the ValueChangeEventArguments.
Custom elements that emit ValueChangeEventArguments need to provide the previous value.
Binding from and to non-existing object properties
Binding used to fail silently if one of the attributes doesn't exist. In case of dictionaries this was intended, because they are often used to bind to persistent storage which is empty by default. But for object properties this can lead to very subtle bugs, e.g. after renaming properties and not updating the attribue names in binding functions.
In 3.0 object properties will be checked for existence by default. Missing dictionaries will continue to be ignored.
You can fine-tune this behavior using the strict: bool | None = None parameter (None: check object properties and ignore dictionaries).
ui.log with unspecified width can collapse inside containers with unspecified width
We noticed that the height of ui.log could be affected by its content, which is unexpected. Therefore we decided to use a scroll area for a more robust layout.
Now the width of ui.log can collapse when placed inside a container with an unspecified width. Either give the container some width or specify the width of ui.log.
ui.clipboard.read() returns None if the clipboard API isn't available
The read() function used to return an empty string if the clipboard API is not available. This was indistinguishable from an empty clipboard.
Now the read() function returns None if the clipboard API is not available.
Remove deprecated code and APIs (#5037 by @falkoschindler)
app.add_static_file and app.add_media_file raises FileNotFoundError instead of ValueError in case of non-existing files. Since FileNotFoundError is NOT a subclass of ValueError, existing code which except ValueError will fail to catch the new FileNotFoundError.ui.aggrid: run_column_method is gone. Use run_grid_method instead.ui.table calling add_rows()/remove_rows() with variable-length arguments no longer works. Pass a list instead or use add_row()/remove_row() for a single row.ui.open is gone. Use ui.navigate.to.nicegui.testing.conftest is gone and you can no longer import it. Use pytest_plugins = ["nicegui.testing.plugin"] instead.element.on parameter js_handler now has type str instead of str | None. You can pass a None and it still works, but your type checker won't be happy, and we can't promise it will work for much longer afterwards.The infrastructure for managing node packages has been improved significantly (#4163, #5021 by @simontaurus, @evnchn, @falkoschindler). The following JavaScript dependencies have been updated to the latest versions (#5034 by @falkoschindler):
Avoid JavaScript error when creating ui.aggrid without custom grid-ready handler (#5141, #5143 by @ZiyadBastaili, @evnchn, @falkoschindler)
ui.aggrid without custom grid-ready handler (#5141, #5143 by @ZiyadBastaili, @evnchn, @falkoschindler)ui.sub_pages render coroutine on cancellation (#5142, #5145 by @platinops, @evnchn, @python-and-novella, @rodja)Let Client.delete() do not cause KeyError if called multiple times (#5119, #5128 by @evnchn, @alydersen, @sonicno1, @rodja)
Client.delete() do not cause KeyError if called multiple times (#5119, #5128 by @evnchn, @alydersen, @sonicno1, @rodja)Improve performance using str.translate instead of repeated str.replace (#5104 by @falkoschindler)
str.translate instead of repeated str.replace (#5104 by @falkoschindler)value=None in ui.checkbox and ui.switch for indeterminate state (#5093 by @daya0576, @falkoschindler)interactive_image layers (#5103 by @mpmX, @falkoschindler)KeyError appearing in the logs (#5090 by @alydersen, @sonicno1, @zbxiany, @evnchn, @maria-ems, @falkoschindler)ui.aggrid not calling the "onGridReady" event handler (#5087 by @denis-x, @falkoschindler)ui.log not scrolling to bottom when pushing multiple lines at once (#3909, #4830, #5112 by @mattie47, @golgitab, @pandabearcodes, @evnchn, @falkoschindler)user.should_see and click for ui.tree (#5068, #5069 by @davetapley, @rodja, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Only start user storage pruning if a storage secret is set (#5077, #5078 by @pascalzauberzeug, @irham-rasyidi, @rodja)
Fix update of hidden ui.plotly elements (#5066 by @Elvynzs, @falkoschindler)
ui.plotly elements (#5066 by @Elvynzs, @falkoschindler)Close unused user storages (#4998, #5074 by @rodja, @falkoschindler)
navigate.to with "mailto" URLs in the presence of ui.sub_pages (#5075 by @rodja, @falkoschindler)ui.sub_pages (#5071, #5072 by @davetapley, @rodja)Handle breaking change of pywebview's dialog type in native mode (#283, #5064 by @jmmlp, @falkoschindler)
props() to take a dict or list without quotes (#706, #4969 by @demo88, @lawrenceakka, @falkoschindler)ui.timer (#5011 by @rodja)ui.color_input in preview mode (#4965, #4966 by @gabriblas, @falkoschindler)ui.dark_mode in combination with ui.run(dark=None) (#3753, #4886, #5042 by @petergaultney, @rodja, @jdoiro3, @evnch, @BaccanoMob, @falkoschindler)ui.plotly (#5033 by @fdion, @falkoschindler)ui.select popups on mobile (#5028, #5031, #5050 by @frankhuurman, @falkoschindler, @rodja)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix bad navigation of relative URLs (#5023, #5025 by @MicaelJarniac, @rodja, @falkoschindler)
ui.plotly's dictionary interface (#5018, #5019 by @samuller, @falkoschindler)ui.sub_pages demo (#5010, #5026 by @MicaelJarniac, @rodja, @falkoschindler)Only use SPA router for relative URLs in ui.navigate.to (#4999, #5002 by @mohankumarelec, @nightwatcher74, @rodja)
ui.navigate.to (#4999, #5002 by @mohankumarelec, @nightwatcher74, @rodja)ui.sub_pages (#5000 by @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Introduce ui.sub_pages for implementing single-page applications (SPA) (#2811, #4821 by @Alyxion, @evnchn, @rodja, @falkoschindler)
ui.sub_pages for implementing single-page applications (SPA) (#2811, #4821 by @Alyxion, @evnchn, @rodja, @falkoschindler)ui.input_chips for input elements with chips (#1649, #4940 by @Stefan2016, @BaccanoMob, @falkoschindler)ui.fab and ui.fab_action for floating action buttons (FAB) (#4813 by @evnchn, falkoschindler)ui.button_dropdown.on_click method for registering click event handlers (#4984 by @falkoschindler)ui.state() outside of ui.refreshable (#4947 by @Atglobal-ML, @falkoschindler)WindowProxy.create_file_dialog (#4953, #4955 by @MaidScientistIzutsumiMarin)ui.sub_pages to greatly speed up the documentation (#4902 by @rodja, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix ui.refreshable causing a RuntimeError on deleted clients (#4948 by @amks1, @evnchn, @chinsaiki, @falkoschindler)
ui.refreshable causing a RuntimeError on deleted clients (#4948 by @amks1, @evnchn, @chinsaiki, @falkoschindler)Allow using other UI frameworks than Quasar (#4858, #4859 by @evnchn, @me21, @kleynjan, @rodja, @falkoschindler)
ui.header update scrollPaddingTop more nicely with a custom header.js component (#4937 by @falkoschindler)ui.code (#4912, #4919, #4921 by @henriknoren, @evnchn, @falkoschindler)ui.select with "add-unique" mode (#4896, #4916 by @BaccanoMob, @falkoschindler)row_key later (#4875, #4893 by @thugentobler, @falkoschindler)ui.aggrid not firing the "gridReady" event (#4883, #4885 by @pimswr, @falkoschindler)KeyboardKey.space to correctly recognize the Space key (#4878, #4879 by @eulerleibniz)ui.spinner with animated Gifs (#4822 by @evnchn)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Allow creating custom error screens (#4538 by @evnchn, @rodja, @falkoschindler)
cache_control_directives in ui.run() (#4532, #4540 by @evnchn, @falkoschindler)ui.codemirror (#4836 by @Yuerchu)ui.mermaid elements on a page (#4852, #4853 by @thetableman, @evnchn)ui.radio elements using the User fixture (#4849 by @barrywhart, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Speed-up page load by skipping ES module shim for modern browsers (#4761, #4801 by @evnchn)
ui.aggrid fully configurable (getters, setters, from_pandas(), from_polars()) (#4782, #4784 by @TeeFiX42, @evnchn, @falkoschindler)ui.download.from_url is called with an absolute URL which can cause problems (#4723, #4779 by @morrowrasmus, @evnchn, @falkoschindler)storage_path and private_mode (#4805, #4811 by @nachobacanful, @evnchn, @falkoschindler)ui.markdown by including codehilite.css more robustly (#4774, #4778 by @platinops, @evnchn, @falkoschindler)search_index.json more aggressively to reduce bandwidth (#4794, #4795 by @falkoschindler, @evnchn)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Allow combining Python handler with js_handler for filtering event arguments (#4577, #4611, #4618, #4689 by @bin-san, @yihuang, @evnchn, @falkoschindl
handler with js_handler for filtering event arguments (#4577, #4611, #4618, #4689 by @bin-san, @yihuang, @evnchn, @falkoschindler)classes, style, and props to ui.log.push (#2508, #4750 by @whoenig, @falkoschindler, @golgitab, @evnchn)ui.table.from_pandas handle dataframes with lists or intervals (#2697, #2744, #4775 by @rohitsathish, @petergaultney, @burnpanck, @lij55, @evnchn, @xaptronic, @falkoschindler)ui.leaflet (#4753, #4754 by @evnchn)ui.select, including a bugfix when closing the popup (#4737, #4757 by @barrywhart, @evnchn, @rodja)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Allow adding SVG layers to `ui.interactive_image` (#4712 by @falkoschindler, @evnchn, @codingpaula)
ui.interactive_image (#4712 by @falkoschindler, @evnchn, @codingpaula)ui.leaflet (#4686, #4707 by @jurstu, @falkoschindler, @evnchn)ui.run (#4679 by @thetableman, @evnchn, @falkoschindler)ui.codemirror not applying changes after inserting emojies (#4575, #4678 by @gbrault, @evnchn, @falkoschindler)ui.markdown or ui.mermaid rendering incorrectly within ui.dialog (#4502, #4518, #4692, #4722 by @ntmarkus, @falkoschindler, @evnchn)ui.refreshable updating wrong target when using ui.state (#3392, #4690, #4691 by @kleynjan, @python-and-novella, @falkoschindler, @evnchn)ui.run_javascript responses (#4725 by @falkoschindler)ui.markdown (#4170 by @Alyxion, @falkoschindler, @evnchn)ElementFilter (#4667, #4682 by @barrywhart, @rodja, @evnchn, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix multi-cursor updates in ui.codemirror (#4701, #4702 by @evnchn, @falkoschindler)
ui.codemirror (#4701, #4702 by @evnchn, @falkoschindler)ui.context_menu with dynamic content (#4695, #4696 by @Anindya088, @evnchn, @falkoschindler)ui.markdown (#4403 by @Lester1989, @NiklasNeugebauer, @evnchn, @falkoschindler)Update documentation on `ui.upload` after a breaking change in Starlette 0.46.0 (#4640 by @yudelevi, @evnchn)
@background_tasks.await_on_shutdown to allow properly awaiting background tasks on shutdown, fixes app reset and RuntimeWarnings in pytests (#4312, #4592, #4608, #4641 by @evnchn, @ReubenVandezande, @rodja, @falkoschindler)element.html_id property (#4633, #4672 by @evnchn, @falkoschindler)ui.codemirror (#4676 by @falkoschindler, @evnchn)ui.codemirror not updating when value changes programmatically (#3337, #4586, #4635 by @zkx06111, @python-and-novella, @zak-45, @cipologic, @evnchn, @falkoschindler)ui.mardown (#4620, #4623, #4625 by @yihuang, @evnchn, @falkoschindler)getElement when called with an HTMLElement (#4674, #4675 by @evnchn)ui.table cells (#4605, #4646 by @rafaelrdealmeida, @evnchn, @falkoschindler)ui.upload after a breaking change in Starlette 0.46.0 (#4640 by @yudelevi, @evnchn)* Note about binding propagation: NiceGUI 2.16.0 improved efficiency of binding propagation by strictly adhering to a Depth-First-Search approach, updating every affected node once and executing the transformation function once. If you are migrating from NiceGUI 2.15.0 or older, there may be extra runs on transformation functions, especially ones in the opposite direction to the current propagation direction, which are no-longer ran in NiceGUI 2.16.0. As a result, you would need to change your code appropriately. We would also like to mention that, for the most stable behaviour across releases, it is best-practice that transform functions have no side-effects and do basic transform operations only. This way, it will not matter how NiceGUI chooses to call them in what order and by how many times.
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Add theme support to `ui.echart` (#4596 by @thetableman, @evnchn, @falkoschindler)
ui.echart (#4596 by @thetableman, @evnchn, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Revert PR #4550 (Avoid RuntimeError while iterating over bindable properties) to prevent server from freezing (@rodja)
ui.matplotlib fill its container (#4595 by @moi90, @falkoschindler, @evnchn)Preload scripts for faster page load (#4505, #4507 by @daya0576, @falkoschindler, @evnchn)
ui.download functions (#4104, #4564 by @marcuslimdw, @falkoschindler, @evnchn)LabelElement mixin to add bindable label property to various elements (#4529 by @weinibuliu, @evnchn, @falkoschindler)on_begin_upload event handler for ui.upload (#4541, #4543 by @jpruente92, @falkoschindler, @evnchn)ui.add_css, ui.add_scss and ui.add_sass set shared=True (#4537 by @evnchn, @falkoschindler)ui.run_javascript in pytests using the User fixture (#4508, #4512 by @chriswi93, @rodja, @falkoschindler)ui.echart from initially rendering twice (#4501, #4535, #4584 by @depley, @evnchn, @falkoschindler)ui.scene.clear() if it contains group scene objects (#4560 by @NiklasNeugebauer, @evnchn, @falkoschindler)background_tasks.create to avoid destroying pending tasks (#4500, #4551 by @JudeMcbeath, @evnchn, @zbxiany, @chriswi93, @jpentland-jrw, @falkoschindler)ui.refreshable and ui.refreshable_method (#4510 by @drkspace)ui.aggrid which requires a @ui.page function (#4570, #4572 by @CodeCubeNeo, @evnchn)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Improve efficiency of async page functions (#4450, #4461, #4462, #4466 by @evnchn, @falkoschindler)
ui.navigate.history to update URL in browser (#4382 by @platinops, @rodja, @falkoschindler)timeout property for ui.notification (#4437, #4444 by @weinibuliu, @falkoschindler)ui.select with multiple values and dictionaries in user test fixture (#4449 by @Daemo00, @rodja, @falkoschindler)ui.json_editor (#4397, #4458 by @natankeddem, @falkoschindler)ui.table.columns (#4418, #4428 by @weinibuliu, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix inconsistency between supported language codes and defined translations (#4417 by @hyoshida-jr, @falkoschindler)
Introduce a `ui.slide_item` element (#4282, #4358 by @void-gfly, @thetableman, @falkoschindler)
ui.slide_item element (#4282, #4358 by @void-gfly, @thetableman, @falkoschindler)ui.rating element (#4347 by @thetableman, @falkoschindler)ui.drawer, ui.left_drawer and ui.right_drawer from ValueElement (#4320, #4334 by @xaptronic, @codingpaula, @falkoschindler)app.add_static_file and app.add_media_file (#4406, #4411 by @ed2050, @rodja)ui.leaflet (#4360 by @platinops, @falkoschindler)ui.leaflet to load before loading plugins (#4395, #4401 by @anthony-arnold)delete method when working with a Redis storage (#4339 by @enigmanoid, @rodja)app.storage.general (#4352, #4355, #4374 by @SHDocter, @zobac, @rod7760, @rodja)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix saving non-serializable data in tab storage (#4313, #4325 by @ReubenVandezande, @falkoschindler, @rodja)
ui.plotly to prevent random resets (#4186, #4317 by @duane-space, @falkoschindler)KeyError in disconnect handler (#4304 by @trivedihoney, @rodja, @chriswi93, @falkoschindler)BindableProperty objects to avoid memory leaks (#4109, #4122 by @andybayer, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Introduce `ui.fullscreen` for controlling fullscreen mode (#4165 by @Alyxion, @falkoschindler)
ui.fullscreen for controlling fullscreen mode (#4165 by @Alyxion, @falkoschindler)@binding.bindable_dataclass decorator to create bindable dataclasses (#3957, #3987 by @balex89, @falkoschindler)selection property for ui.table to allow changing the selection type (#4296 by @DevMax007, @falkoschindler)ui.leaflet from resetting center and zoom when browser resizes (#4182, #4265 by @qutang, @nathanWagenbach, @falkoschindler)🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Fix problems in native mode caused by app.main_window being undefined (#4244 by @raffaellof, @falkoschindler)
app.main_window being undefined (#4244 by @raffaellof, @falkoschindler)Introduce redis storage which syncs between multiple instances, sponsored by @alydersen (#1606, #4074 by @rodja, @hany, @juledwar, @Alyxion, @falkosch
ui.line_plot.push() and fix potential matplotlib warning (#4192 by @drkspace, @falkoschindler)ui.clipboard.read_image() (#4144 by @liunux4odoo, @falkoschindler)ui.input ignoring "keydown.-" events (#4209, #4210 by @ronniebax, @falkoschindler)Avoid browser storage being shared across clients when accessing via On Air (GHSA-v6jv-p6r8-j78w by @streamcfd, @rodja)
ui.editor (#4148 by @Alyxion)Retransmit messages after short connection interruptions to keep client in sync (#3143, #3199 by @FeldrinH, @afullerx, @me21, @falkoschindler, @rodja)
app.timer (#3187, #3225, #4091 by @adosikas, @falkoschindler, @v479038280, @rodja)getHtmlElement function (#3019, #4087, #4123 by @chrschorn, @Alyxion, @falkoschindler)User fixture (#4080, #4098 by @randy-seng, @rodja)fastapi_docs parameter in ui.run() (#4050 by @Yuerchu, @falkoschindler)ui.leaflet from oscillating between two locations (#4061, #4121 by @hal2000gh, @falkoschindler)ui.menu_item (#4107, #4115 by @ed2050, @falkoschindler)ui.input to ui.date with date range selection (#4077 by @Mte90, @falkoschindler)and all our other sponsors and contributors for supporting this project!
🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!
Your coding agent can read these notes before it upgrades. Set up the MCP server →