pypi nicegui 4.0.0
v4.0.0

3 hours ago

NiceGUI 4.0 is a major release focused on cleaning up: it removes the APIs deprecated during 3.x, corrects a few defaults and catches up on the bundled JavaScript libraries. Nothing changes as fundamentally as the auto-index page did in 3.0, so most apps should only need small adjustments. Some changes don't raise any error, though, so please read the migration guide before upgrading.

How to upgrade

  1. Update to the latest 3.19 release, run your app and tests, and fix all deprecation warnings. They name the replacement for most of what is removed in 4.0.
  2. Check the new minimum requirements below.
  3. Upgrade and run a type checker like mypy or pyright. It flags many leftovers, e.g. sanitize=None, before the first run.
  4. Go through the behavior changes below, because most of them won't show up as an error.

New features and enhancements

Bugfixes

⚠️ Migration guide

Minimum requirements

Requirement 3.19 4.0 Ticket
Python 3.10 3.11 #6393 by @falkoschindler, @evnchn
FastAPI 0.109.1 0.143.1 #6373, #6416 by @falkoschindler, @evnchn, @Melvin0hd
Pillow (matplotlib extra) 12.3 13.0 #6467 by @falkoschindler
Chrome / Edge 111 121 #6440 by @falkoschindler, @evnchn
Safari 16.4 17.2 #6440 by @falkoschindler, @evnchn
HTTP client httpx httpx2 #5876, #6024, #6398 by @falkoschindler, @evnchn, @KrilleGH, @cervelas, @samuller
  • httpx is no longer installed. Apps importing it have to declare it themselves or switch to httpx2, which has the same API. The testing API now uses httpx2 types: User(client: httpx2.AsyncClient), and UserDownload.http_responses and UserDownload.next() hold or return httpx2.Response. In general, please declare every package you import instead of relying on what NiceGUI happens to pull in.
  • Python 3.15 on glibc-based Linux needs glibc 2.39 or newer (e.g. Ubuntu 24.04+, Debian 13+) for orjson's wheels. Older systems need a source build with Rust and a C compiler.
  • The Docker image now ships pandas 3, polars 2 and numpy 2.5, and no longer includes pytz. Install it yourself or switch to zoneinfo.

Behavior changes

These changes mostly don't raise an error, so your code won't tell you about them.

  1. Old prop names are ignored (#6397 by @falkoschindler, @evnchn)

    Snake_case and camelCase prop names are no longer migrated to kebab-case. .props('html_columns=[0]'), for example, now sets a prop nobody reads, so the feature silently stops working and nothing warns about it anymore. Constructor arguments are not affected. Please search your code for the old names:

    All removed prop names
    Element Old New
    all elements resource_path resource-path
    all elements dynamic_resource_path dynamic-resource-path
    ui.aggrid html_columns html-columns
    ui.codemirror highlightWhitespace highlight-whitespace
    ui.codemirror lineWrapping line-wrapping
    ui.colors dark_page dark-page
    ui.colors custom_colors custom-colors
    ui.echart enable_3d enable-3d
    ui.fullscreen requireEscapeHold require-escape-hold
    ui.header add_scroll_padding add-scroll-padding
    ui.leaflet draw_control draw-control
    ui.leaflet hide_drawn_items hide-drawn-items
    ui.leaflet additional_resources additional-resources
    ui.markdown resource_name resource-name
    ui.markdown use_mermaid use-mermaid
    ui.scene background_color background-color
    ui.scene click_events click-events
    ui.scene drag_constraints drag-constraints
    ui.scene, ui.scene_view camera_params camera-params
    ui.scene, ui.scene_view camera_type camera-type
    ui.scene, ui.scene_view show_stats show-stats
    ui.scene_view scene_id scene-id
  2. Cleared text inputs hold '' instead of None (#6421, #6435 by @falkoschindler, @evnchn, @emmanuelprecieuxargent)

    This affects ui.input, ui.textarea, ui.date_input, ui.time_input and ui.color_input. Replace value is None with not value, which works before and after. A model using None as "empty" can keep it with input.bind_value(model, 'x', forward=lambda v: v or None, backward=lambda v: v or ''). ui.number and ui.select keep None.

  3. run.cpu_bound uses "spawn" by default (#6117, #6419 by @falkoschindler, @evnchn)

    Workers start from a fresh import and don't inherit module globals. The function must be importable in the worker: functions defined under if __name__ == '__main__':, via python -c, stdin, a zipapp or a package's __main__.py now fail with BrokenProcessPool, and the actual error only appears in the worker's log. To keep the old behavior on Linux, set run.process_pool_start_method = 'fork' before ui.run().

  4. run.io_bound and run.cpu_bound raise asyncio.CancelledError instead of returning None (#6420 by @falkoschindler, @evnchn)

    Code checking if result is None: to detect a shutdown or cancellation has to catch the exception or let it propagate.

  5. Element IDs in the DOM change from c12 to ng-12 (#6375, #6378, #6395 by @falkoschindler, @evnchn)

    Replace hard-coded IDs in CSS, JavaScript or tests with element.html_id or getHtmlElement(element.id). Custom components stripping the prefix with id.slice(1) have to use parseHtmlId(id). The implicit browser globals like c12 are gone, and the global of ui.scene for Selenium tests is now scene_<id>.

  6. Spot lights in ui.scene default to decay=2.0 (#6243, #6366, #6417 by @falkoschindler, @evnchn, @python-and-novella)

    They now fall off like in Three.js with 1/r² instead of 1/r, so at a distance of 5 units they are five times dimmer. Pass decay=1.0 to restore the old look, or increase intensity.

  7. table.pagination shows the pagination controls (#6235, #6275 by @evnchn, @SaadZahem, @falkoschindler)

    This also applies to tables created without pagination, and .props('hide-pagination') no longer survives a later assignment. Use pagination_controls=False instead.

  8. Custom scene objects and map layers need a with block in handlers and timers (#6345, #6361, #6401 by @evnchn, @falkoschindler, @wolfgang-aura)

    Constructing a custom Object3D or Layer subclass outside of with scene: used to find the last scene or map touched anywhere in the process. Now it raises a RuntimeError. The same applies to on_click=scene.box, which fetches the constructor early: use on_click=lambda: scene.box(). Object3D.current_scene and Layer.current_leaflet are now private.

  9. Screen.should_not_contain waits for the text to disappear (#6432 by @falkoschindler, @evnchn)

    It used to fail immediately if the text was still shown, and then observed for 0.5 s. A check that something must not show up after an action now needs duration=0.5 to keep its strength. The parameter wait is a deprecated alias for duration.

  10. Observable collections have no __dict__ (#6409, #6410 by @evnchn, @falkoschindler)

    vars() and setting arbitrary attributes fail on ObservableDict, ObservableList, ObservableSet and on element.props, classes and style. Subclasses without their own __slots__, like PersistentDict, are not affected.

  11. Bindings no longer poll bindable properties written past BindableProperty.__set__ (#6412, #6434 by @evnchn, @falkoschindler)

    This only concerns values set via __dict__.update, a custom __copy__ or a descriptor subclass that skips super().__set__. Regular assignments still propagate.

  12. WEB_CONCURRENCY above 1 raises a ValueError (#6467 by @falkoschindler)

    NiceGUI doesn't support multiple workers. With reload=True, the default, uvicorn used to ignore the variable with a warning, and with reload=False, the app logged a message and exited. Please remove the variable from the environment of your app.

  13. ui.timer callbacks must not swallow the cancellation (#6470 by @falkoschindler)

    A callback catching asyncio.CancelledError without re-raising it can delay the shutdown, which 3.19 tolerated. run.io_bound and run.cpu_bound no longer do this themselves (#6420), so this only concerns your own code.

Removed APIs

These fail with an error or are flagged by a type checker (#6397 by @falkoschindler, @evnchn):

Removed Use instead
sanitize=None in ui.chat_message and ui.interactive_image True (default), False or a callable
nicegui.dataclasses.KWONLY_SLOTS kw_only=True, slots=True
coroutine=... in background_tasks.create() and create_lazy() awaitable=...
Awaitables in lifecycle registrations like app.on_startup(startup()) The function itself: app.on_startup(startup)
'checkboxRenderer' in ui.aggrid 'cellDataType': 'boolean' and 'editable': True
ui.tab outside of ui.tabs Nest it, it now raises a RuntimeError
ValueChangeEventArguments without previous_value Pass it, as custom elements now have to
Object3D subclasses without component= Add component= or use a built-in base class
Object3D instantiated directly or with a type string The built-in objects or a custom subclass
Object3D.type and Object3D.data No replacement
nicegui.elements.scene.scene_objects nicegui.elements.scene.objects
Positional optional arguments of ui.scene and ui.scene_view Keywords (width and height of ui.scene and scene of ui.scene_view stay positional)
ui.add_scss and ui.add_sass, including the bundled Sass compiler ui.add_css, as CSS supports nesting natively
Props.add_rename (#6464, #6471 by @shi-felix, @falkoschindler) Map the old name yourself; add_warning only drops the key and warns

ui.codemirror(language='Jinja2') still works, but is deprecated and logs a warning. Please use language='Jinja' (#6467 by @falkoschindler).

Bundled libraries

If you use these elements, or target their internals with custom CSS or JavaScript, please check the respective changes:

Quasar 2.18.5 → 2.35.2 (#6440 by @falkoschindler, @evnchn)
  • ui.menu and ui.tooltip keep their placement while scrolling and stay attached to their anchor.
  • Dialogs close on mousedown on the backdrop instead of click.
  • A click listener on ui.item or ui.chip implies clickable.
  • Icons in side sections of list items are lighter.
  • The table pagination label uses an en dash (1–2 of 3).
  • See the Quasar release notes from 2.19.0 to 2.35.2 for details.
AG Grid 34.2.0 → 36.2.0 (#6444 by @falkoschindler, @evnchn)
  • Misspelled or unknown options no longer produce console warnings. Add 'ValidationModule' to modules to get them back, now also as an overlay on the grid.
  • Many internal class names changed (e.g. ag-body-viewport, ag-center-cols-container), and theme and RTL classes moved to a parent element, so custom CSS may need an update.
  • The fontWeight theme parameter defaults to 400. Set fontWeight: 'inherit' to inherit the page's font weight.
  • The pagination panel has an input field for the page number.
  • content-visibility: auto is no longer applied by default (enableContentVisibilityAuto brings it back).
  • The internal class .ag-center-cols-container no longer exists. Custom CSS or tests selecting rows through it have to use .ag-row directly.
  • See the upgrade guides for AG Grid 35 and AG Grid 36 for details.
plotly.js 3.1.1 → 4.2.0 (#6327, #6441, #6467 by @falkoschindler, @evnchn)
  • The scattermapbox, choroplethmapbox and densitymapbox traces, the mapbox subplot and mapboxAccessToken are removed. Use the *map equivalents.
  • rgb() with 0–1 fractions and hsv() strings are no longer accepted, and invalid CSS color strings are rejected.
  • layout.geo.fitbounds defaults to 'locations', and scattermap and densitymap auto-fit center and zoom.
  • MathJax v2 is no longer supported.
  • splom.axis.matches defaults to true, overlaying axes default to tickmode: 'sync', and the double-click delay increased from 300 to 500 ms.
  • hoveranywhere and clickanywhere events return actual data values for xvals and yvals.
  • The new "Share chart" button, which sends chart data to cloud.plotly.com, is hidden by default. ui.plotly({'data': ..., 'config': {'showSendToCloud': True}}) brings it back.
three.js r180 → r187 (#6443, #6467 by @falkoschindler, @evnchn)
  • PBR materials, as created by scene.gltf() for most models, conserve energy better: rough materials look slightly brighter.
  • Custom scene objects setting matrixAutoUpdate = false and writing matrix directly must also set matrixWorldNeedsUpdate = true.
  • Object3D has its own dispose(), so custom subclasses with a dispose() should call super.dispose().
xterm.js 5.5.0 → 6.0.0 (#6445 by @falkoschindler, @evnchn)
  • The default font is the browser's monospace font. Pass {'fontFamily': 'courier-new, courier, monospace'} to restore the old look. As the line height may differ, fit() can yield a different number of rows.
  • The viewport and scrollbar were replaced, so custom CSS targeting .xterm-viewport or .xterm-scroll-area may need an update.
  • overviewRulerWidth moved to overviewRuler: {'width': ...}, and windowsMode and fastScrollModifier were removed.
  • Alt+arrow keys are no longer translated to Ctrl+arrow.
  • The selection is cleared when the terminal is resized vertically.
  • See the xterm.js release notes for details.
nipplejs 0.10.2 → 1.0.4 (#6446 by @falkoschindler, @evnchn)
  • The outer circle of ui.joystick is no longer rendered at half opacity. Pass a semi-transparent back color like color={'front': 'blue', 'back': 'rgba(0, 0, 255, 0.5)'} to restore the old look.
  • The option maxNumberOfNipples was renamed to maxNumberOfJoysticks.
  • The raw move event data no longer contains identifier but gains baseDelta, and the raw start and end events now contain data.
  • See the nipplejs migration guide for details.

Dependencies

  • Update the remaining Python and JavaScript dependencies, including uvicorn 0.54, Starlette 1.10 and websockets 17 in the lock file (#6467 by @falkoschindler)

    Other JavaScript dependencies
    • @tailwindcss/browser: 4.1.13 → 4.3.4
    • @unocss/reset and @unocss/runtime: 66.6.0 → 66.12.0
    • socket.io: 4.8.1 → 4.8.4
    • vue: 3.5.22 → 3.5.44
    • @codemirror/language-data: 6.5.1 → 6.5.2
    • @uiw/codemirror-themes-all: 4.25.2 → 4.25.12
    • @babel/runtime: 7.28.4 → 7.29.10
    • vanilla-jsoneditor: 3.10.0 → 3.13.0
    • mermaid: 11.16.1 → 11.17.2
    • d3-transition: 3.0.2 (new)
    • sass: 1.94.0 (removed)

Documentation

Testing

Infrastructure


Special thanks to our top sponsors DiscoLike Inc., TestMu AI and Lechler GmbH ✨

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!

Don't miss a new nicegui release

NewReleases is sending notifications on new releases.