Skip to content

IPYNB to Markdown Converter

Turn a .ipynb file into one Markdown file for a README, a docs site or a blog: code in fenced blocks, outputs under it, and charts embedded so nothing else needs copying. Made in your browser.

v2026.10.16

Open a Jupyter notebook

No Jupyter, no install, no upload.

Sample

bike-rides.ipynb

Python 3 (ipykernel)Python 3.12.4nbformat 4.512 cells · 154 words · 1 picture

# Bike rides and rain: a short walkthrough

A week of **made-up** numbers from a small town's bike-share scheme. It shows what a notebook can hold: Markdown, code, a table, a chart, maths, a warning and, at the end, an error.

```python
import pandas as pd
import matplotlib.pyplot as plt
```

```python
rides = pd.DataFrame({
    "day": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
    "trips": [412, 438, 395, 451, 288, 230],
    "rain_mm": [0.0, 1.2, 4.5, 0.0, 9.8, 14.1],
}).set_index("day")
rides
```

<div>

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right">
      <th></th>
      <th>trips</th>
      <th>rain_mm</th>
    </tr>
    <tr>
      <th>day</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Mon</th>
      <td>412</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>Tue</th>
      <td>438</td>
      <td>1.2</td>
    </tr>
    <tr>
      <th>Wed</th>
      <td>395</td>
      <td>4.5</td>
    </tr>
    <tr>
      <th>Thu</th>
      <td>451</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>Fri</th>
      <td>288</td>
      <td>9.8</td>
    </tr>
    <tr>
      <th>Sat</th>
      <td>230</td>
      <td>14.1</td>
    </tr>
  </tbody>
</table>
</div>

## How busy was the week?

The mean number of trips per day is

$$\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i$$

where $n$ is the number of days and $x_i$ is the number of trips on day $i$.

```python
mean = rides["trips"].mean()
print(f"Mean trips per day: {mean:.1f}")
print(f"Busiest day: {rides['trips'].idxmax()}")
```

```
Mean trips per day: 369.0
Busiest day: Thu
```

```python
import warnings

if rides["rain_mm"].max() > 10:
    warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
```

```
/tmp/ipykernel_4120/2715331896.py:4: UserWarning: Saturday had over 10 mm of rain; the gauge reads high in storms.
  warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
```

## Rain and riding

Fewer people rode on wet days. Pearson’s correlation coefficient measures how closely two columns move together, from −1 to 1:

$$r = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_i (x_i - \bar{x})^2}\,\sqrt{\sum_i (y_i - \bar{y})^2}}$$

```python
fig, ax = plt.subplots(figsize=(6.4, 3.6))
ax.bar(rides.index, rides["trips"], color="#4878d0")
ax.set_ylabel("Trips")
ax.set_title("Bike-share trips per day")
plt.show()
```

![Output of cell 8](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAFoBAMAAAA1HFdiAAAAAXNSR0IB2cksfwAAADBQTFRFAAAA//… (4 KB more on this line)

```python
round(rides["trips"].corr(rides["rain_mm"]), 3)
```

```
-0.979
```

### A day that isn’t there

There is no Sunday in the data, so asking for it raises a `KeyError`. This is what a traceback looks like:

```python
rides.loc["Sun"]
```

```
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[7], line 1
----> 1 rides.loc["Sun"]

File ~/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
   3810     return self._engine.get_loc(casted_key)
   3811 except KeyError as err:
-> 3812     raise KeyError(key) from err

KeyError: 'Sun'
```

### What next

- [x] Load a week of rides
- [x] Chart them
- [ ] Add the weekend’s weather from the town’s own gauge

> Made-up data, made for this example. Any resemblance to a real town is a coincidence.

Bike rides and rain: a short walkthrough

A week of made-up numbers from a small town's bike-share scheme. It shows what a notebook can hold: Markdown, code, a table, a chart, maths, a warning and, at the end, an error.

In [1]
import pandas as pd
import matplotlib.pyplot as plt
In [2]
rides = pd.DataFrame({
    "day": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
    "trips": [412, 438, 395, 451, 288, 230],
    "rain_mm": [0.0, 1.2, 4.5, 0.0, 9.8, 14.1],
}).set_index("day")
rides
Out [2]
trips rain_mm
day
Mon 412 0.0
Tue 438 1.2
Wed 395 4.5
Thu 451 0.0
Fri 288 9.8
Sat 230 14.1

How busy was the week?

The mean number of trips per day is

xˉ=1n∑i=1nxi\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i

where nn is the number of days and xix_i is the number of trips on day ii.

In [3]
mean = rides["trips"].mean()
print(f"Mean trips per day: {mean:.1f}")
print(f"Busiest day: {rides['trips'].idxmax()}")
Mean trips per day: 369.0
Busiest day: Thu
In [4]
import warnings

if rides["rain_mm"].max() > 10:
    warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")
/tmp/ipykernel_4120/2715331896.py:4: UserWarning: Saturday had over 10 mm of rain; the gauge reads high in storms.
  warnings.warn("Saturday had over 10 mm of rain; the gauge reads high in storms.")

Rain and riding

Fewer people rode on wet days. Pearson’s correlation coefficient measures how closely two columns move together, from −1 to 1:

r=∑i(xi−xˉ)(yi−yˉ)∑i(xi−xˉ)2 ∑i(yi−yˉ)2r = \frac{\sum_i (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_i (x_i - \bar{x})^2}\,\sqrt{\sum_i (y_i - \bar{y})^2}}

In [5]
fig, ax = plt.subplots(figsize=(6.4, 3.6))
ax.bar(rides.index, rides["trips"], color="#4878d0")
ax.set_ylabel("Trips")
ax.set_title("Bike-share trips per day")
plt.show()
Image output of cell 8
In [6]
round(rides["trips"].corr(rides["rain_mm"]), 3)
Out [6]
-0.979

A day that isn’t there

There is no Sunday in the data, so asking for it raises a KeyError. This is what a traceback looks like:

In [7]
rides.loc["Sun"]

KeyError: 'Sun'

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[7], line 1
----> 1 rides.loc["Sun"]

File ~/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py:3812, in Index.get_loc(self, key)
   3810     return self._engine.get_loc(casted_key)
   3811 except KeyError as err:
-> 3812     raise KeyError(key) from err

KeyError: 'Sun'

What next

  • Load a week of rides
  • Chart them
  • Add the weekend’s weather from the town’s own gauge

Made-up data, made for this example. Any resemblance to a real town is a coincidence.

Your notebook is read on this device and never uploaded. Check it yourself

How this works: Method, 5 sources, Checked against 2 worked examples,

How this works

Method

The .ipynb file is read in the browser as the nbformat JSON it is: every cell, its last outputs and its attachments. Markdown is drawn with GitHub Flavored Markdown and maths with KaTeX, code is coloured by the notebook’s language, and each output shows its richest format, as JupyterLab chooses. All HTML from the notebook is sanitised first and nothing from other sites is loaded. PDF comes from the browser’s print engine; HTML, script and Markdown files are written in the page.

How it’s tested

2 worked examples for this page are checked by automated tests before every release: given the inputs, the tool must show the expected answer.

Privacy

On your device. Nothing you enter or open leaves this browser. About privacy labels

Changes

  • First published: open a .ipynb file with its outputs, maths and colours, and save it as PDF, HTML, a script or Markdown.

Every change on What’s new

How we make toolsReport a mistake

A notebook as plain text

Markdown is how a notebook travels into places that aren’t Jupyter: a README on GitHub, a static docs site, a blog post, a note in Obsidian. The Markdown cells come across exactly as written, so headings, lists, links and maths between dollar signs stay the same, and every code cell becomes a fenced block tagged with the notebook’s language so it is highlighted wherever it lands.

Outputs follow their code. Printed text, results and tracebacks go in plain fenced blocks, with the terminal colour codes stripped. A pandas table goes in as HTML, which Markdown allows and GitHub draws as a table. Maths outputs become display maths between double dollar signs, and JSON outputs a json block.

Pictures are the one choice to make. jupyter nbconvert --to markdown writes each chart to its own file in a folder beside the .md, which you then have to keep together. This converter embeds them in the Markdown as data instead, so one file holds everything; VS Code, Obsidian, Typora and most static site generators show them. GitHub does not show embedded images, so for a README choose Leave pictures out and add the charts as files.

Interactive widgets and JavaScript charts can’t be written as Markdown, so they become a one-line note in italics.

Good to know

How to convert a Jupyter notebook to Markdown3 steps, all on your device
  1. Open the notebook. Drop the .ipynb file on the card or choose it. The Markdown appears below the notebook as you will get it.
  2. Choose what happens to pictures. Embed them in the file as data, so the .md is complete on its own, or leave them out for a light text file.
  3. Download or copy. Press Save .md, or copy the Markdown and paste it where it is going.

Frequently Asked Questions

Will the Markdown work on GitHub?

The text, code and tables will. GitHub doesn’t show images embedded as data, so for a README choose Leave pictures out and commit the charts as image files.

Is the maths kept?

Yes. Maths in Markdown cells is copied as written, and maths outputs become display maths between double dollar signs. GitHub, Obsidian, Jupyter Book and many static site generators draw them.

Can I leave the outputs out?

Yes. Turn off Include outputs and the file holds the Markdown and the code alone. Folding outputs on the page only changes the view, not the file.

Why do my tables appear as HTML?

pandas writes its tables as HTML, and Markdown allows HTML, so they are kept as they are rather than rebuilt. Scripts and styles in them are removed first.

“Jupyter” is a trademark of Project Jupyter, used here only to say which files this viewer opens. Binary Decimal is not affiliated with or endorsed by Project Jupyter.