#6290·nicegui

Lazily-imported ESM elements fail when first created in an event handler (importmap already sent)

Author: evnchnCreated Aug 15, 2026Updated Aug 23, 2026
Labelsbug

Filed by Claude Code on evnchn's behalf.

First Check

  • I added a very descriptive title here.
  • This is not a security issue (those should be reported via the security advisory instead).
  • This is not a Q&A. I am sure something is wrong with NiceGUI or its documentation.
  • I used the GitHub search to find a similar issue and came up empty.

Example Code

python
from nicegui import ui

OPTIONS = {'title': False, 'series': [{'data': [1, 2, 3]}]}


@ui.page('/')
def index():
    # ui.highchart(OPTIONS)   # <-- uncomment: the button below then works
    ui.button('add chart', on_click=lambda: ui.highchart(OPTIONS))


ui.run()

Requires pip install nicegui-highcharts (tested with the released 3.4.0).

Description

Open the page and click add chart.

I expected a chart to appear. Instead nothing appears, and the browser console shows:

TypeError: Failed to resolve module specifier "nicegui-highcharts".
Relative references must start with either "/", "./", or "../".
TypeError: getElement(...)?.[element.update_method] is not a function

Uncomment the commented line — so that some chart already exists when the response is built — and the very same button then works.

So the trigger is not "created dynamically" as such: it is that the first chart on the page is created after the response was built. Once any chart has been created during the initial render, every later one works.

This means ui.highchart(...) inside a button handler, a dialog, ui.refreshable, or ui.sub_pages silently renders nothing on a page that had no chart to begin with.

Broken since 3.11.0 — 3.10.0 is fine.

Version matrix (released wheel, one-line toggle)
NiceGUI first chart created in handler a chart also created in the response
3.10.0 renders renders
3.11.0 BROKENFailed to resolve module specifier "nicegui-highcharts" renders
3.16.0 BROKENFailed to resolve module specifier "nicegui-highcharts" renders

Checked by counting .highcharts-container elements before and after the click and asserting the count increments, rather than by eye:

=== nicegui 3.16.0 + nicegui-highcharts 3.4.0 ===
charts before click: 0   (expected 0)
charts after  click: 0   (expected 1)
console error: TypeError: Failed to resolve module specifier "nicegui-highcharts". Relative references must start with either "/", "./", or "../".
console error: TypeError: getElement(...)?.[element.update_method] is not a function
AssertionError: FAILED: clicking added no chart — still 0
Mechanism — why 3.11.0 is the boundary

The import map is rendered into the initial HTML (templates/index.html):

xml
<script type="importmap">
  {"imports": {{ imports | safe }}}
</script>

The nicegui_highcharts entry gets there via register_esm(), which runs when the package is imported. In 3.11.0 ui.highchart became lazily resolved through a __getattr__ table (ui.py:51'highchart': ('.elements.highchart', 'highchart'), alongside the new setup_esm_package helper in dependencies.py), so the package — and therefore register_esm() — only executes on first attribute access.

When that first access happens inside an event handler, the import map for that client has already been serialized, and an importmap cannot be amended after page load. Before 3.11.0 the element was imported eagerly at import time, so the specifier was always present.

I have not looked for a fix, so I don't know whether the right shape is registering ESM modules eagerly, injecting a module shim on demand, or documenting the constraint.

NiceGUI Version

3.16.0 (also reproduced on 3.11.0; 3.10.0 is unaffected)

Python Version

3.12.11

Browser

Chrome

Operating System

macOS

Additional Context

Tested with nicegui-highcharts 3.4.0 from PyPI, Chromium 151.0.7922.34 via Playwright, macOS 26.5.2 arm64.

Scope of what I checked: Chromium only (not Firefox/Safari).

Correction (I originally speculated the built-in ESM elements would behave the same way — they do not). ui.echart created in a handler on a chart-free page works fine. The ten built-in ESM elements are registered at import time:

python
>>> from nicegui import ui
>>> from nicegui.dependencies import esm_modules
>>> [m.name for m in esm_modules.values()]
['nicegui-leaflet', 'nicegui-xterm', 'nicegui-scene', 'nicegui-mermaid', 'nicegui-joystick',
 'nicegui-echart', 'nicegui-codemirror', 'nicegui-plotly', 'nicegui-aggrid', 'nicegui-json-editor']

nicegui-highcharts is absent from that list, because it is an external package reached only through the lazy try: from nicegui_highcharts import highchart in nicegui/elements/highchart.py, so its register_esm() does not run until first attribute access. So this looks specific to the external-package path rather than to ESM elements in general.

This is the same lazy-import change that produces the version-dependent behaviour in nicegui-highcharts PR 32, which is how I ran into it.