Skip to content

Cannot run certain JS libraries in VSCode Jupyter through IPython.display.HTML but it works fine on the web browser #16786

@sachinhosmani

Description

@sachinhosmani

Environment data

  • VS Code version: 1.101.2
  • Jupyter Extension version (available under the Extensions sidebar): v2025.6.0
  • Python Extension version (available under the Extensions sidebar): v2025.10.0
  • OS (Windows | Mac | Linux distro) and version: Mac Ventura 13.5
  • Python and/or Anaconda version: Python 3.11.13
  • Type of virtual environment used (N/A | venv | virtualenv | conda | ...): Conda 23.11.0
  • Jupyter server running: Local

Expected behaviour

I should be able to run JS libraries like viz-js, Plotly, Leaflet on VSCode notebooks the same way they run on web-based Jupyter notebook. My issue does not relate to downloading the scripts from remote servers (which hasn't been a problem).

Actual behaviour

When I try to run the library viz-js usingIPython.display.HTML it doesn't load the variable Viz successfully. The same problem applies to several other libraries like Plotly, Chartjs and TensorflowJs.
However, the exact same code works perfectly on Jupyter notebook on the browser.

And, interestingly some libraries like d3 load properly in both VSCode and web-based Jupyter notebook. So it likely isn't a problem with my code.

Steps to reproduce:

  1. Run this minimal reproducible example in VSCode Jupyter cell (this steps should be alone enough to see the problem)
import requests
import random
import string
from IPython.display import HTML

# Download the Viz.js standalone script
vizjs_code = requests.get("https://cdn.jsdelivr.net/npm/@viz-js/viz@3.14.0/lib/viz-standalone.min.js").text
print('length of vizjs_code:', len(vizjs_code))
print('first 100 characters of vizjs_code:')
print(vizjs_code[:100])

# Generate a random ID for the div (8 characters, letters + digits)
random_id = ''.join(random.choices(string.ascii_letters + string.digits, k=8))

html_content = f"""
<div id="{random_id}" style="font-family: monospace; white-space: pre;"></div>
<script>
{vizjs_code}
</script>
<script>
(function() {{
    let attempts = 0;
    let maxAttempts = 5;
    const outputDiv = document.getElementById("{random_id}");

    function logMessage(msg, isError) {{
        console[isError ? "error" : "log"](msg);
        outputDiv.textContent = msg;
    }}

    function checkViz() {{
        attempts++;
        if (typeof Viz !== 'undefined') {{
            logMessage("✅ Viz is loaded!", false);
        }} else if (attempts < maxAttempts) {{
            logMessage("❌ Viz is NOT loaded, retrying.", true);
            setTimeout(checkViz, 1000);
        }} else {{
            logMessage("❌ Viz is NOT loaded.", true);
        }}
    }}

    checkViz();
}})();
</script>
"""

HTML(html_content)

All it does is successfully download the viz-js script (verifiable from the print statements that it is successful) and tries to inject it into a script element.

Observe how

  • it can successfully download the script based on the print statements for the length of the script and the first 100 characters of it
  • it cannot find the Viz variable
  1. Change the above code to load d3 instead (and it starts working now)
import requests
import random
import string
from IPython.display import HTML

# Download the d3.js standalone script
d3js_code = requests.get("https://d3js.org/d3.v7.min.js").text
print('length of d3js_code:', len(d3js_code))
print('first 100 characters of d3js_code:')
print(d3js_code[:100])

# Generate a random ID for the div (8 characters, letters + digits)
random_id = ''.join(random.choices(string.ascii_letters + string.digits, k=8))

html_content = f"""
<div id="{random_id}" style="font-family: monospace; white-space: pre;"></div>
<script>
{d3js_code}
</script>
<script>
(function() {{
    let attempts = 0;
    let maxAttempts = 5;
    const outputDiv = document.getElementById("{random_id}");

    function logMessage(msg, isError) {{
        console[isError ? "error" : "log"](msg);
        outputDiv.textContent = msg;
    }}

    function checkd3() {{
        attempts++;
        if (typeof d3 !== 'undefined') {{
            logMessage("✅ d3 is loaded!", false);
        }} else if (attempts < maxAttempts) {{
            logMessage("❌ d3 is NOT loaded, retrying.", true);
            setTimeout(checkd3, 1000);
        }} else {{
            logMessage("❌ d3 is NOT loaded.", true);
        }}
    }}

    checkd3();
}})();
</script>
"""

HTML(html_content)

Observe how it works properly now.

  1. Run the same code on a browser-based Jupyter notebook. It works properly for both viz-js and d3.

  2. Change the script tag to <script src="https://cdn.jsdelivr.net/npm/@viz-js/viz@3.14.0/lib/viz-standalone.min.js"></script> instead of injecting the string. Nothing changes in the behaviour described so far.

  3. Try another library plotly (https://cdn.plot.ly/plotly-2.30.0.min.js which should export the variable Plotly) and observe that it doesn't work on VsCode but works on Jupyter. (similar behaviour as viz-js)

  4. Try another library MathJax (https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js which shoudl export the variable MathJax) and it works properly like d3.

Conclusion: Some libraries like viz-js and plotly don't work in VSCode but work in browser based Jupyter notebooks. Some libraries like d3 and MathJax work fine in both.

Logs

Output for Jupyter in the Output panel (ViewOutput, change the drop-down the upper-right of the Output panel to Jupyter)

No logs seen

Metadata

Metadata

Assignees

Labels

bugIssue identified by VS Code Team member as probable bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions