Skip to content

Fix Plotly CDN rendering in nbviewer#5504

Open
mklilley wants to merge 2 commits intoplotly:mainfrom
mklilley:main
Open

Fix Plotly CDN rendering in nbviewer#5504
mklilley wants to merge 2 commits intoplotly:mainfrom
mklilley:main

Conversation

@mklilley
Copy link

Fix Plotly CDN rendering in nbviewer (RequireJS “mismatched anonymous define()”)

Related issue: #5314 (reported by @mklilley)

Summary

This PR fixes a bug where Plotly figures render correctly in Google Colab, but fail to render when the same notebook is viewed on nbviewer. The failure presents as:

  • Uncaught Error: Mismatched anonymous define() module (RequireJS)
  • Uncaught ReferenceError: Plotly is not defined

Root Cause

nbviewer loads RequireJS on the page. When Plotly is included via a plain CDN <script src="/api/proxy?url=https%3A%2F%2Fcdn.plot.ly%2Fplotly-...min.js">, the Plotly bundle may detect an AMD/CommonJS environment (e.g. define.amd, module, exports) and register as an anonymous AMD module. RequireJS then throws the “mismatched anonymous define()” error, and Plotly does not end up attached to window.Plotly, causing subsequent Plotly.newPlot(...) calls to fail.

What This PR Changes

  1. Guard Plotly CDN loading against RequireJS/AMD/CommonJS detection

    • In plotly/io/_html.py, when include_plotlyjs="cdn", the generated HTML now:
      • Saves the current values of define.amd, module, and exports.
      • Temporarily disables them while loading the CDN plotly-*.min.js.
      • Restores the original values immediately after the script loads.
    • This forces Plotly’s CDN bundle to initialize in “browser global” mode so window.Plotly is reliably defined in nbviewer.
  2. Remove redundant connected renderer global-init module import

    • In plotly/io/_base_renderers.py, HtmlRenderer.activate() no longer injects an extra <script type="module">import ...</script> for connected renderers.
    • This import is redundant (the figure HTML already loads Plotly when connected) and can interact poorly with environments that already manage JS loaders.
  3. Tests updated

    • tests/test_io/test_renderers.py updated to validate:
      • The RequireJS guard is included in connected HTML outputs.
      • The connected renderer init output no longer embeds/imports Plotly JS.
    • Related suites were also run to ensure the new CDN HTML is still correct.

Why This Approach

  • It’s targeted to the actual nbviewer failure mode: RequireJS + CDN script loading.
  • It keeps the existing include_plotlyjs="cdn" behavior (still uses versioned CDN URL + SRI), while making it robust in AMD loader contexts.
  • The guard is reversible (restores the environment after Plotly loads) to minimize interference with other notebook JS.

Files Changed

  • plotly/io/_html.py
  • plotly/io/_base_renderers.py
  • tests/test_io/test_renderers.py

How To Reproduce / Verify (from the issue)

  1. Create a notebook that uses a connected renderer (e.g. pio.renderers.default = "notebook_connected+colab").
  2. Save it with outputs.
  3. Open the notebook on nbviewer.
  4. Before this PR: RequireJS mismatch error + Plotly is not defined, plot doesn’t render.
  5. After this PR: no RequireJS mismatch; plot renders normally.

Code PR

  • I have read through the contributing notes (CONTRIBUTING.md) and understand the structure of the package. In particular, if my PR modifies code of plotly.graph_objects, my modifications concern the code generator and not the generated files.
  • I have added tests or modified existing tests.
  • For a new feature, I have added documentation examples (not applicable; this is a bugfix).
  • I have added a CHANGELOG entry if changing anything substantial (not added; please advise if maintainers want an entry).
  • For a new feature or a change in behavior, I have updated the relevant docstrings in the code (not applicable; change is limited to generated HTML for include_plotlyjs="cdn" to avoid nbviewer/RequireJS breakage).

nbviewer injects RequireJS; when plotly.js is loaded via a plain CDN <script>, it
can be treated as an anonymous AMD/CommonJS module, triggering "Mismatched
anonymous define()" and leaving `window.Plotly` undefined.

- Wrap the CDN script tag with a guard that temporarily disables AMD/CommonJS
  detection and then restores it.
- Drop the redundant connected renderer global-init module import.
- Update renderer tests to cover the RequireJS guard and new init output.
@mklilley
Copy link
Author

mklilley commented Feb 12, 2026

I have attempted a fix of this - full disclosure I used codex to do most of the work because I don't know the library well enough to attempt a fix myself. The original issue has been sitting around for a while so I thought it was better to attempt a fix with AI than leave it unresolved. I hope that's ok. I'm happy to take input/feedback.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant