{"id":11608,"library":"pyodide","title":"Pyodide: Python in the Browser & Node.js","description":"Pyodide is an open-source project that compiles the CPython interpreter to WebAssembly, enabling the full execution of Python directly within web browsers and Node.js environments without requiring a server backend. Its current stable release is 0.29.3, with frequent patch and minor updates; a significant version change to 314.0.0a1 signals alignment with upcoming Python 3.14 releases and a revised ABI stabilization strategy. Key differentiators include seamless bidirectional interoperability between JavaScript and Python, comprehensive support for a vast array of popular scientific Python libraries (such as NumPy, Pandas, and Matplotlib), and a lightweight package manager (`micropip`) that allows dynamic installation of pure Python wheels from PyPI. This enables advanced client-side data processing, interactive visualizations, and powerful educational tools to run entirely within the browser, significantly expanding Python's reach in web development.","status":"active","version":"0.29.3","language":"javascript","source_language":"en","source_url":"https://github.com/pyodide/pyodide","tags":["javascript","python","webassembly","typescript"],"install":[{"cmd":"npm install pyodide","lang":"bash","label":"npm"},{"cmd":"yarn add pyodide","lang":"bash","label":"yarn"},{"cmd":"pnpm add pyodide","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The primary asynchronous function to initialize the Pyodide runtime. It returns a `PyodideInterface` instance. Pyodide officially supports ESM.","wrong":"import loadPyodide from 'pyodide'; // Not a default export","symbol":"loadPyodide","correct":"import { loadPyodide } from 'pyodide';\n// Or for older Node.js/CJS:\n// const { loadPyodide } = require('pyodide');"},{"note":"TypeScript type for the object returned by `loadPyodide`, providing access to Python execution and utilities.","symbol":"PyodideInterface","correct":"import type { PyodideInterface } from 'pyodide';"},{"note":"Executes Python code as a string. Must be `await`ed as it returns a Promise. Standard output and error can be redirected via `pyodide.setStdout` and `pyodide.setStderr` for custom handling.","wrong":"pyodide.runPython('print(\"Hello\")'); // Missing await, as it returns a Promise","symbol":"pyodide.runPython","correct":"await pyodide.runPython(`\n  import sys\n  print('Hello from Python!', file=sys.stderr)\n`);"},{"note":"Loads pre-compiled Pyodide packages from a CDN. Returns a Promise. For pure Python packages, `micropip.install` is generally preferred for dependency resolution.","wrong":"pyodide.loadPackage('pandas'); // Missing await","symbol":"pyodide.loadPackage","correct":"await pyodide.loadPackage('numpy');"},{"note":"Used for installing Python packages, especially pure Python wheels, from PyPI within the Pyodide environment. `micropip` itself must first be loaded as a package.","wrong":"await pyodide.install('requests'); // No direct 'install' method on pyodide object","symbol":"micropip.install","correct":"await pyodide.loadPackage('micropip');\nconst micropip = pyodide.pyimport('micropip');\nawait micropip.install('matplotlib');"}],"quickstart":{"code":"import { loadPyodide } from 'pyodide';\n\nasync function main() {\n  console.log('Initializing Pyodide...');\n  const pyodide = await loadPyodide({\n    indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.25.0/full/' // Always pin the version!\n  });\n  console.log('Pyodide initialized.');\n\n  // Redirect Python stdout/stderr to JS console\n  pyodide.setStdout((text) => console.log('PY_STDOUT:', text));\n  pyodide.setStderr((text) => console.error('PY_STDERR:', text));\n\n  // Run simple Python code\n  console.log('Running Python code...');\n  const pythonResult = await pyodide.runPython(`\n    import sys\n    print('Hello from Python!')\n    x = 10\n    y = 20\n    x + y\n  `);\n  console.log('Python computed:', pythonResult);\n\n  // Load a Python package and use it\n  console.log('Loading NumPy...');\n  await pyodide.loadPackage('numpy');\n  const numpyVersion = pyodide.runPython('import numpy; numpy.__version__');\n  console.log('NumPy version:', numpyVersion);\n\n  const jsArray = [1, 2, 3, 4, 5];\n  pyodide.globals.set('js_array', jsArray);\n\n  const pythonArrayResult = await pyodide.runPython(`\n    import numpy as np\n    py_array = np.array(js_array)\n    print(f'Python array sum: {py_array.sum()}')\n    py_array.tolist()\n  `);\n  console.log('Python processed array:', pythonArrayResult);\n\n  // Clean up PyProxy objects if necessary (important for preventing memory leaks)\n  pyodide.destroy();\n  console.log('Pyodide instance destroyed.');\n}\n\nmain().catch(err => {\n  console.error('An error occurred:', err);\n});","lang":"typescript","description":"This quickstart demonstrates how to load Pyodide, execute basic Python code, install a scientific package like NumPy, and facilitate data exchange between JavaScript and Python, all within a browser environment."},"warnings":[{"fix":"Refer to the official Pyodide documentation for the specific version you are upgrading to and update API calls accordingly, focusing on `loadPyodide`, `runPython`, and `pyodide.FS`.","message":"Pyodide versions 0.x have undergone significant API redesigns, particularly around 0.17.0, which introduced a more robust `asyncio` integration and overhauled core APIs. Older code relying on pre-0.17 APIs will likely break.","severity":"breaking","affected_versions":"<0.17.0"},{"fix":"Optimize loading by using the minimal `pyodide-core` package where possible, preloading only essential packages, and deferring heavier libraries (e.g., scikit-learn) until needed. Utilize service workers and IndexedDB for caching to improve subsequent load times.","message":"Pyodide bundles a full CPython interpreter and many scientific libraries, leading to a large initial download size (hundreds of MB for full distribution). This can severely impact page load performance.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Always run Pyodide within a Web Worker. Use `pyodide.runPythonAsync` for asynchronous Python code and design Python functions to be non-blocking where possible. Pass UI updates back via postMessage.","message":"Long-running Python computations executed directly on the main thread can block the browser's UI, leading to unresponsiveness.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure that all `PyProxy` objects created on the JavaScript side are explicitly destroyed using `pyProxy.destroy()` when they are no longer needed. For simple data, use `toJs()` with `dict_converter` to convert Python objects to plain JavaScript structures.","message":"When transferring Python objects to JavaScript, Pyodide creates `PyProxy` objects. Failing to explicitly call `.destroy()` on these proxies (especially for frequently created or large objects) can lead to memory leaks over time.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Handle `loadPyodide` failures by refreshing the worker or page. The Pyodide team suggests internal retry logic for network requests as a potential future improvement, but currently, clean recovery is difficult without a full reset.","message":"Calling `loadPyodide()` again after a previous attempt failed (e.g., due to a network error) will often throw an `Error: Pyodide is already loading` or similar, preventing retry logic.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Monitor official announcements for the new versioning scheme and ABI changes. Be prepared to rebuild or update Python packages for new major Pyodide releases. Pin Pyodide versions to avoid unexpected breaking changes.","message":"Pyodide is expected to transition to a new versioning scheme, potentially aligning with Python minor versions (e.g., 3.14.x) instead of the current 0.x series. This will be accompanied by a new ABI, which means packages built for older Pyodide ABIs will not be compatible with new major Pyodide versions.","severity":"breaking","affected_versions":">=0.x, upcoming 3.x"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure `loadPyodide` is only called once per worker or page lifecycle. If a load fails, a full page/worker reload might be necessary, as internal state is not easily reset. Implement retry logic around the entire worker initialization if network stability is a concern.","cause":"Attempting to call `loadPyodide` multiple times, especially after a prior load failed or is still in progress, without proper cleanup.","error":"Uncaught (in promise) Error: Pyodide is already loading"},{"fix":"Configure your bundler to correctly polyfill Node.js modules for browser environments. For Webpack, this might involve `resolve.fallback`. Ensure `pyodide` is treated as an external module or that its Node.js-specific parts are properly handled if you are only targeting the browser. In some cases, updating your bundler configuration or Pyodide version can resolve this.","cause":"This error typically occurs when bundling Pyodide for a browser environment using tools like Webpack or Vite, as Pyodide's internal Node.js compatibility shims (which use `fs/promises`) are not correctly polyfilled or excluded for the browser target.","error":"Module not found: Can't resolve 'fs/promises'"},{"fix":"To use Pyodide, load it via JavaScript in a browser or Node.js environment. Do not try to `pip install pyodide`. If you need the Python `pyodide` module's API for type hints in a native Python project, install `pyodide-py` via `pip install pyodide-py`. When running within Pyodide, the `pyodide` module is available by default, and other packages are installed using `micropip.install`.","cause":"This error occurs when trying to `pip install pyodide` in a native Python environment. The `pyodide` npm package is a JavaScript distribution, not a Python package intended for native Python installation. The Python module `pyodide` (and `piplite`) is only available within the Pyodide runtime or the `pyodide-py` package for type-checking.","error":"ModuleNotFoundError: No module named 'pyodide'"},{"fix":"Convert Python objects to JavaScript-compatible types before returning them. For NumPy arrays, use `tolist()` (`py_array.tolist()`). For Pandas DataFrames, use `to_json()` or `to_dict()` (`df.to_json()`). For custom objects, provide a `__js_repr__` method or manually convert to basic Python types (lists, dicts) before converting to JS using `pyodide.toJs(python_object)`.","cause":"Attempting to return complex Python objects (like NumPy arrays, Pandas DataFrames, or custom Python classes) directly to JavaScript, which expects JSON-serializable types or primitive values. JavaScript does not automatically know how to serialize arbitrary Python objects.","error":"TypeError: object of type 'XXX' is not JSON serializable (from Python code)"}],"ecosystem":"npm"}