100% Local · Free · No Sign-up

Convert Jupyter Notebooks (.ipynb) to Markdown

Convert ipynb to md without Python, nbconvert, or a running kernel. The notebook JSON is parsed against the nbformat schema in a Web Worker on your own device, and each cell is mapped to its Markdown equivalent — prose verbatim, code into language-tagged fences, outputs beneath the cell that produced them.
Drop your Jupyter file here to convert to MarkdownDrag & drop files here, or click anywhere to browse · .ipynb

Processing happens 100% in your browser — nothing is uploaded.

100% Local Processing — Your files never leave your device

How to convert a Jupyter notebook to Markdown

  1. Drop the file: drag your .ipynb onto the dropzone, or click to browse. Nothing is uploaded — the file is read locally.
  2. Local processing: the notebook JSON is parsed in a Web Worker, then walked cell by cell — markdown cells verbatim, code cells into fenced blocks, outputs rendered by MIME type.
  3. Copy or download: take the Markdown to your clipboard, or save it as a .md file ready for GitHub, Obsidian, Hugo or a docs repo.

Why Convert Jupyter to Markdown with MD Convert?

  • Cell-type dispatch, not text scraping

    Each entry in the notebook's cells array is handled by its cell_type. Markdown cells are emitted verbatim so headings, links and tables survive byte-for-byte; code cells become fenced blocks tagged with the kernel language read from metadata.language_info, so Python, Julia and R notebooks all highlight correctly.

  • MIME-aware output rendering

    Stream output becomes a fenced block under its cell, image/png results are inlined as base64 data URIs so Matplotlib figures travel with the file, and text/plain is used for DataFrame and array reprs. Error outputs are kept with ANSI colour escape codes stripped from the traceback.

  • LaTeX and math passed through untouched

    Inline $...$ and display $$...$$ expressions are never rewritten, because math is not a Markdown construct and rewriting it would guess at your renderer. The output works with whatever math pipeline you already use in Obsidian, a static site generator or a docs platform.

  • Safe for unpublished research and client notebooks

    Parsing runs entirely in your browser, so a notebook holding customer data, an unredacted API key in a cell, or a pre-publication result never crosses the network. There is no upload, no queue, and no account.

Jupyter to Markdown: Before and After

Two cells of raw nbformat JSON and the Markdown they produce. Note that `source` is a line array, the fence language comes from the kernel metadata, and the LaTeX in the prose cell is passed through untouched.

Input · ipynb (nbformat JSON)
{"cells": [
  {"cell_type": "markdown", "source": ["## Gradient check\n", "Loss is $\\mathcal{L} = \\sum_i (y_i - \\hat{y}_i)^2$.\n"]},
  {"cell_type": "code", "execution_count": 3,
   "source": ["print(loss(y, y_hat))\n"],
   "outputs": [{"output_type": "stream", "text": ["0.0421\n"]}]}
], "metadata": {"language_info": {"name": "python"}}}
Output · Markdown
## Gradient check

Loss is $\mathcal{L} = \sum_i (y_i - \hat{y}_i)^2$.

```python
print(loss(y, y_hat))
```

```
0.0421
```

Understanding the Jupyter Format: nbformat 4

Format
Jupyter Notebook Format (nbformat)
Specification
nbformat 4
Media type
application/x-ipynb+json

Technical Specifications & Cell Mapping Rules

  • Markdown cells: emitted verbatim. Tables, math, HTML and headings survive byte-for-byte, because the cell source is already Markdown.
  • Code cells: wrapped in language-tagged fences read from metadata.language_info.name, with fallbacks to kernelspec.language and then python. Python, Julia and R notebooks are tagged correctly.
  • MIME outputs: stream output (stdout/stderr) becomes a plain fence beneath its cell. image/png is inlined as a base64 data URI. Other rich types fall back to text/plain, which preserves DataFrame and array reprs. ANSI color escape codes are stripped from error tracebacks.
  • Interactive outputs and widgets: ipywidgets, Plotly and Bokeh outputs need a live JavaScript kernel and are not recoverable in static Markdown.
  • Raw cells and attachments: raw cells are skipped. Attachments referenced as attachment: URIs need manual relinking, because they do not resolve outside the notebook.
  • Dropped metadata: execution counts, cell IDs, tags and slide directives have no Markdown equivalent and are not emitted.

Developer Alternative: CLI (nbconvert) & Python Extraction

For CI pipelines or environments that already run Python, nbconvert is the canonical tool. It can also execute the notebook first and write images to a companion directory.

Bash (nbconvert CLI)
# Canonical CLI conversion using nbconvert
jupyter nbconvert --to markdown notebook.ipynb
Python (nbconvert API)
# Programmatic conversion using nbconvert Python API
import nbformat
from nbconvert import MarkdownExporter

with open("notebook.ipynb") as f:
    nb = nbformat.read(f, as_version=4)

md_exporter = MarkdownExporter()
body, _ = md_exporter.from_notebook_node(nb)
print(body)

Math and LaTeX pass-through

Inline $...$ and display $$...$$ math is left exactly as authored. Math is not a Markdown construct, so transforming it would guess at your renderer. If an expression shows as literal dollar signs, the renderer's math extension is disabled; the source text is byte-identical to the notebook cell.

How You Can Verify the Privacy Claim

Browser-only

Zero server upload

Conversion runs inside your browser tab. Open DevTools, switch to the Network panel, and convert a file: for every format except URL to Markdown you will see no request carrying your document — because there is no endpoint to send it to.

Web Worker

Off the main thread

Heavy parsing is dispatched to a Web Worker, so a 500-page PDF or a large spreadsheet never freezes the interface. Everything is plain JavaScript — no native plugin, no WebAssembly toolchain, nothing to install.

No account

Nothing to sign up for

No login, no quota, no paywall, and no tracking tied to your files. Analytics are cookieless and aggregate only. Read the privacy policy for the full data-flow breakdown, including the one proxied exception.

The parsers doing the work

No proprietary black box: each format is handled by a widely audited open-source library, running client-side at the version pinned in our lockfile.

Jupyter to Markdown — FAQ

Are code cell outputs and plots included in the Markdown?

Yes. Stream output (anything printed to stdout or stderr) becomes a fenced block beneath its cell, and image/png outputs are inlined as base64 data URIs so a Matplotlib figure remains visible in the Markdown with no sidecar image files. Outputs with no PNG fall back to their text/plain representation, which is what preserves a pandas DataFrame repr.

How is this different from jupyter nbconvert --to markdown?

nbconvert is the canonical tool and does more: it can execute the notebook first, apply templates, and write images to a companion directory. It also needs a Python environment with Jupyter installed. This converter needs a browser, works on a machine with no Python, and never transmits the notebook — which matters when you are not permitted to upload it.

Does LaTeX and math survive the conversion?

Yes, verbatim. Inline $...$ and display $$...$$ expressions are copied through unchanged. If math renders as literal dollar signs in your target, the math extension is disabled in that renderer — the source text is byte-identical to the notebook cell.

What happens to raw cells, widgets and interactive plots?

Raw cells are skipped, because their content only means something to a specific nbconvert target such as a LaTeX preamble. Widget state and interactive output from ipywidgets, Plotly or Bokeh cannot be recovered either — they require a live kernel and a JavaScript runtime that a static .md file does not have.

In-Depth Jupyter Guides