Legacy browser support for NiceGUI 3.8: classic-script + es-module-shims loading paths for old browsers (Chrome 56+, Firefox 52+, iOS 12 Safari)
Project description
nicegui-legacy-browser
Legacy browser support for NiceGUI 3.8.x — an ASGI middleware (polyfills, JS transpilation/bundling, CSS transforms) that makes NiceGUI run on old browsers via two auto-selected loading paths. Verified floor: Chrome 56+, Firefox 52+, iOS 12 Safari (see the status note below for what's tested vs. aspirational).
Status (2026-07): two loading paths, auto-selected by the browser's User-Agent.
- Native-ES-module browsers (Safari 12+, Chrome 61+, modern) → the es-module-shims path (validated incl. real iOS 12 Safari).
- No-native-module browsers (Chrome 55–60, Firefox <60, Chromecast) → a classic-script bundle — verified mounting + interactive on Chromium 56, Firefox 52, and real iOS 12 Safari.
The floor is Chrome ~55/56, not 49 (the bundler can't lower syntax further without an added Babel pass), so Chrome 49 / Chromecast-1st-gen is not verified — treat those rows below as aspirational. Lazy-loaded components now load on the classic path via prebuilt per-component bundles (
static/_classic/components/): leaflet is verified mounting on Chromium 56 (both eager and on-demand). aggrid/echart/plotly/mermaid build clean the same way but are deferred (browser-unverified, 1.5–7 MB) — uncomment a line inbuild_classic.shto ship one. SeeFINDINGS.mdand issue #2 for the per-component table.
Why?
NiceGUI 3.8.0 uses modern web features that break in old browsers:
| Feature | Required by | Minimum Browser |
|---|---|---|
<script type="importmap"> |
Vue loading | Chrome 89, Safari 16.4 |
<script type="module"> |
Vue/component loading | Chrome 61, Safari 11 |
CSS @layer |
Style ordering | Chrome 99, Safari 15.4 |
Optional chaining ?. |
nicegui.js, Vue 3, components | Chrome 80, Safari 13.1 |
Nullish coalescing ?? |
nicegui.js | Chrome 80, Safari 13.1 |
globalThis |
Vue setup | Chrome 71, Safari 12.1 |
async/await |
Event handling | Chrome 55, Safari 10.1 |
Object.entries() |
nicegui.js | Chrome 54 |
String.replaceAll() |
nicegui.js | Chrome 85, Safari 13.1 |
This package bridges the gap by:
- Transpiling all JS (Vue 3, Quasar, nicegui.js, components) via Babel to remove modern syntax
- Polyfilling missing APIs (
globalThis,Object.entries,replaceAll, etc.) - Shimming ES modules and import maps via es-module-shims
- Transforming CSS to remove
@layer, logical properties,revert-layer, etc.
Hard Requirements
Vue 3's reactivity system requires native Proxy support. This cannot be polyfilled. Therefore:
- Chrome 49+ ✅ (Proxy added in Chrome 49)
- Safari 10+ / iOS 10+ ✅
- IE 11 ❌ (No Proxy — fundamentally impossible)
Quick Start
pip install nicegui-legacy-browser # pulls in a compatible nicegui
from nicegui import ui
from nicegui_legacy_browser import enable
enable() # call BEFORE ui.run()
@ui.page('/')
def index():
ui.label('Works in old browsers!')
ui.run()
That's it — no build step. The transpiled/bundled JS ships prebuilt inside the
package, so there is no Node.js requirement at install or runtime. enable()
picks the right loading path per browser automatically (see below). You only need
the nicegui-legacy-build command (and Node) to regenerate the bundles after
bumping NiceGUI — see Regenerating the bundles.
How It Works
Architecture
Browser Request
│
▼
┌─────────────────────────────┐
│ LegacyBrowserMiddleware │ ASGI middleware wrapping NiceGUI
│ (intercepts all responses) │
├─────────────────────────────┤
│ HTML Transform: │
│ • Inject polyfills.js │ ← globalThis, Object.entries, replaceAll, ...
│ • Inject es-module-shims │ ← Polyfills import maps for old browsers
│ • importmap → importmap-shim
│ • module → module-shim │
│ • Strip CSS @layer │
│ • Remove modulepreload │
│ • Remove Tailwind v4 syntax│
├─────────────────────────────┤
│ JS Transform: │
│ • Serve Babel-transpiled │ ← ?. → null checks, ?? → ternaries,
│ versions of all .js │ async/await → generators
├─────────────────────────────┤
│ CSS Transform: │
│ • Strip @layer │
│ • revert-layer → revert │
│ • Logical props → physical │ ← margin-block-start → margin-top
│ • Remove :dir() │
└─────────────────────────────┘
│
▼
NiceGUI App (unmodified)
Prebuilt bundles (no build needed to use)
The lowered JS ships committed inside the package — both the static/_transpiled/
files (Babel-transpiled originals, for the es-module-shims path) and the
static/_classic/ bundles (esbuild classic-script bundles, for no-native-module
browsers). Installing the package is enough; enable() serves them directly. Node.js
is not a dependency of normal use — it lives behind the optional [build] extra
and is only needed to regenerate the bundles (below).
No Hooks Required in NiceGUI
This package uses standard ASGI middleware via app.add_middleware(). No modifications to NiceGUI's source code are needed. The middleware:
- Intercepts HTML responses to inject polyfills and transform inline CSS
- Intercepts JS responses to serve transpiled versions
- Intercepts CSS responses to strip
@layerand transform logical properties - Serves its own static files (polyfills.js, es-module-shims.js)
Browser Compatibility Matrix
| Browser | Version | Status | Notes |
|---|---|---|---|
| Chrome | 49-54 | ⚠️ Unverified | Below the classic-bundle floor (~55/56); needs a Babel pass — see issue #3 |
| Chrome | 55-60 | ✅ Works | Classic-script bundle (verified on Chromium 56) |
| Chrome | 61-79 | ✅ Works | Import maps shimmed |
| Chrome | 80-88 | ✅ Works | Import maps shimmed |
| Chrome | 89+ | ✅ Works | Native import maps |
| Safari | 12 (iOS 12) | ✅ Works | Import maps shimmed |
| Safari | 13-13.1 | ✅ Works | Import maps shimmed |
| Safari | 14-16.3 | ✅ Works | Import maps shimmed |
| Safari | 16.4+ | ✅ Works | Native import maps |
| Chromecast 1st gen | ~Chrome 49-55 | ⚠️ Unverified | Aspirational; ~Chrome 49 is below the classic floor — see issue #3 |
| IE 11 | — | ❌ Impossible | No Proxy support |
Configuration
enable(
transpiled_dir=None, # Path to pre-transpiled JS (auto-detected)
legacy_static_prefix='/_nicegui_legacy', # URL prefix for polyfill files
auto_build=True, # Auto-run build if transpiled files missing
target='default', # Browser target: chrome49, chrome55, safari12, default
)
Build Targets
| Target | Transforms | Use Case |
|---|---|---|
default |
Everything (chrome 49 + safari 12) | Maximum compatibility |
chrome49 |
async/await + all ES2020+ | Chromecast 1st gen |
chrome55 |
ES2020+ only (keeps async/await) | Chromecast 2nd gen |
chrome61 |
ES2020+ only (keeps modules) | Older desktops |
safari12 |
ES2020+ only | iOS 12 devices |
Known Limitations
-
Tailwind CSS v4: Not supported in old browsers. The JIT compiler requires Chrome 111+. Use Tailwind v3 or UnoCSS with a compatible preset instead.
-
Mermaid diagrams: Some mermaid dist files (3 out of ~40) fail Babel transpilation due to complex generator patterns. The originals are used as fallback — they may not work in Chrome < 55.
-
WebGL (3D scenes):
ui.scene()requires WebGL which has varying support on old hardware. Chromecast 1st gen does not support WebGL. -
CSS
gapin flexbox: Not supported in Chrome < 84. Some spacing may be slightly off. This is a cosmetic issue only. -
Performance: The middleware adds ~1ms of overhead per HTML response for the transform. JS/CSS transforms add negligible overhead due to caching.
-
File size: Polyfills add ~64KB (es-module-shims) + ~12KB (polyfills.js) to the initial page load.
Testing with Old Browsers
On x86_64 Linux (with Docker)
# Download old Chromium snapshot for testing
# Chrome 55 (last without async/await native support)
docker run -d -p 4444:4444 selenium/standalone-chrome:3.141.59
# Or download a Chromium binary directly:
# 1. Find revision: https://omahaproxy.appspot.com (version 55.0.2883.87 → revision ~433059)
# 2. Download: https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/433059/chrome-linux.zip
On macOS
Use Safari's Responsive Design Mode or install old Safari versions via Xcode.
On actual devices
For Chromecast testing:
pip install catt
catt cast_site http://your-server:8080/
Development
git clone <repo>
cd nicegui-legacy-browser
pip install -e ".[test]" # bundles are committed; no build needed to run tests
pytest tests/ -v
To regenerate the bundles after a NiceGUI bump, see
Regenerating the bundles (adds the [build] extra + Node).
Version Compatibility
Pinned to nicegui>=3.7,<3.9. The shipped bundles (static/_transpiled/,
static/_classic/) are lowered copies of a specific NiceGUI release's static
assets, so they are coupled to that release's JS. A NiceGUI bump can reshape that
markup — the classic transform will fail loudly if it no longer recognises the
page (rather than silently serving a broken page), signalling a regenerate is due.
Regenerating the bundles
Only needed after bumping NiceGUI (or to add a lazy component). Requires Node.js,
pulled in via the optional [build] extra:
pip install -e ".[build]"
nicegui-legacy-build --target default # es-module-shims path (Babel)
bash nicegui_legacy_browser/build/build_classic.sh # classic-bundle path (esbuild)
Commit the regenerated static/_transpiled/ + static/_classic/ so users still
get a build-free install.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nicegui_legacy_browser-0.1.1.tar.gz.
File metadata
- Download URL: nicegui_legacy_browser-0.1.1.tar.gz
- Upload date:
- Size: 7.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a069b7e7d4613690c80fc6d94db78a6fe604aa66b9889d6bf546a6fe46090f7
|
|
| MD5 |
c3bf068c8728d0e374725f01fdc32937
|
|
| BLAKE2b-256 |
5de6278b2526cee380fe5b6795d6687e4d90ac3f002686df177023b3f509b53c
|
File details
Details for the file nicegui_legacy_browser-0.1.1-py3-none-any.whl.
File metadata
- Download URL: nicegui_legacy_browser-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
598ee5915f6ef80decc82fcea995298abe4af5307fd0841105b12d6180e687fe
|
|
| MD5 |
9930460945cdbc215f1c4a7fddf8e2d7
|
|
| BLAKE2b-256 |
71504af67db6add228ee05bb954a35071a04bbe490498aceb905337aaf3b5bbe
|