Runpeek Python progress bars

Progress bars in Jupyter notebooks with tqdm

For a widget progress bar in Jupyter, import tqdm 4.70.1 from tqdm.auto with ipywidgets 8.1.9 installed; Runpeek's drop-in from tqdm_live import tqdm needs no widgets, drawing a text bar in the cell and streaming it live to a link you can open on your phone. ipywidgets must be in the kernel's environment: if you get the IProgress not found error, run %pip install ipywidgets inside a cell and restart your kernel.

6 min readtqdm and progress bars

At a glance

  • from tqdm.auto import tqdm draws a widget in a notebook and a text bar in a terminal.
  • Runpeek's from tqdm_live import tqdm draws the text bar in the cell and shows it live on your phone, with no ipywidgets needed.
  • IProgress not found means ipywidgets is missing from the kernel's environment: run %pip install ipywidgets, then restart the kernel.
  • tqdm.notebook always builds a widget; in a plain terminal it prints one static 0% line.
  • Text bars nest badly in notebook output; widget bars nest cleanly.

Use tqdm.auto in notebooks

Import tqdm from tqdm.auto instead of the base package. In Jupyter Notebook, JupyterLab and VS Code notebooks, tqdm.auto gives you the ipywidgets bar, and when you run the same script in a terminal it gives you the normal text bar, so you don't have to change your imports when moving code between a notebook and a .py file.

Python
from time import sleep
from tqdm.auto import tqdm

for row in tqdm(range(200), desc="rows"):
    sleep(0.01)

You'll also see from tqdm.notebook import tqdm and from tqdm.autonotebook import tqdm in older code. tqdm.notebook always builds the widget, so in a plain terminal it prints a single static 0% line instead of a live bar. tqdm.autonotebook works like tqdm.auto but prints a TqdmExperimentalWarning in notebooks; tqdm.auto avoids that warning.

The widget bar changes colour to show its state:

  • Blue while the loop is running.
  • Green when the loop finishes.
  • Red if the loop stops on an exception or keyboard interrupt. The notebook bar doesn't close itself when that happens, so it stays red in the cell output.
  • Light blue when there's no total and no ETA.

To change the width of the widget bar, pass an integer: ncols=480 makes it 480 pixels wide. tqdm's README also shows strings such as ncols="100%", but in tqdm 4.70.1 those raise a TypeError (open tqdm issue #1533).

Follow a notebook run from your phone

Runpeek streams a notebook's progress bar to a link you can open anywhere, so you can walk away from a long run. In our test on 2026-10-30, from tqdm_live import tqdm drew the text bar in the cell and printed a runpeek ▶ https://runpeek.app/b/<uuid> link showing the live bar in a browser, no account needed.

The kernel stays alive after a cell finishes, so a few things work differently than in a script:

  • Call tqdm_live.start_run("name") to group every bar under a single /r/ link. The run stays open until you shut the kernel down, which marks it finished.
  • An exception inside a cell doesn't mark the run as failed on its own: in our test the run kept going after the traceback and later ended as finished. Catch the error and call tqdm_live.end_run("failed", exit_code=1) if you want failures reported right away.
  • After start_run, tqdm_live.notify(url, stall_after_minutes=10) sends an alert to a Discord, Slack or other webhook URL when no update arrives for 10 minutes while the run is open. An idle kernel after the loop counts too, so shut the kernel down when the work is done.

Fix "IProgress not found"

When ipywidgets isn't installed in your kernel, tqdm.notebook raises an ImportError:

IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html

If you're using tqdm.auto without ipywidgets, it won't crash: it shows the same message as a TqdmWarning and falls back to the text bar. Either way, the fix is to install ipywidgets into the same environment that your Jupyter kernel is running.

Python
%pip install ipywidgets

Using %pip inside a notebook cell guarantees the package goes into the active kernel's environment. You can also run pip install "tqdm[notebook]" in a terminal inside that same environment, which pulls in ipywidgets>=6. Once the install finishes, restart the kernel so tqdm picks up the widget classes; if the bar still doesn't draw in JupyterLab, reload the browser tab.

JupyterLab, VS Code, QtConsole and Spyder

tqdm.auto makes its choice inside the kernel. Code running in an IPython kernel gets the widget bar, unless tqdm spots QtConsole or Spyder through the QT_API or SPYDER* environment variables or the shell's class name. A plain ipython terminal gets the text bar. Beyond that, the kernel can't tell which frontend is attached, so the frontend has to render the widget:

  • Jupyter Notebook and JupyterLab: installing ipywidgets in the kernel's environment also installs jupyterlab_widgets, which sets up the frontend. If JupyterLab runs in a different environment from the kernel, install jupyterlab_widgets where JupyterLab runs and ipywidgets in each kernel's environment.
  • VS Code: the Jupyter extension renders widgets by downloading their scripts from jsDelivr or unpkg, set by jupyter.widgetScriptSources. If those sites are blocked, the widget may not render.
  • QtConsole and Spyder: since tqdm 4.68.3, tqdm.auto detects these and gives them the text bar, because they don't render ipywidgets. The same release added JupyterLite detection.
  • jupyter console: it runs the same kernel as a notebook, so in our test tqdm.auto still picked the widget bar, which a terminal can't draw. Use from tqdm import tqdm there.

Nested bars and reusing a bar across cells

For nested loops, wrap both the outer and inner iterables with tqdm. Pass leave=False on the inner bar if you want it to disappear each time the inner loop finishes instead of leaving a stack of green bars behind.

Python
from time import sleep
from tqdm.auto import tqdm, trange

for epoch in trange(3, desc="epochs"):
    for batch in tqdm(range(100), desc="batches", leave=False):
        sleep(0.005)

By default, a widget bar appears in the output of whichever cell created it. If you create a bar in one cell and update it in a later cell, the updates still happen back up in the first cell's output.

To control where the bar renders, create it with display=False, then call display(pbar.container) from IPython.display in the cell where you actually want it to show up. You can also reuse that same bar across multiple loops or cells by calling pbar.reset(total=...) instead of creating a new one each time.

Python
from tqdm.notebook import tqdm

pbar = tqdm(total=100, desc="shared", display=False)

# in a later cell
from IPython.display import display

display(pbar.container)
for _ in range(100):
    pbar.update(1)

# reuse the same bar for the next loop
pbar.reset(total=50)

Widget bar or text bar

When widgets won't render in your environment, from tqdm import tqdm is the fallback. It prints the plain text bar into the cell output without needing ipywidgets. JupyterLab's output area handles the carriage return the bar uses to redraw its line, but it doesn't handle the cursor-up codes that nested text bars need, so nested text bars leave extra lines behind.

ImportIn a notebookIn a terminalWithout ipywidgets
Runpeek: from tqdm_live import tqdmText bar plus a live linkText bar plus a live linkText bar plus a live link
from tqdm.auto import tqdmWidget barText barText bar plus a warning
from tqdm.notebook import tqdmWidget barOne static lineImportError
from tqdm.autonotebook import tqdmWidget bar plus a warningText barText bar plus a warning
from tqdm import tqdmText barText barText bar

FAQ

Why does tqdm print a new line for every update in my notebook?

Notebook output areas redraw a single text bar with a carriage return, but they can't move the cursor up, so nested or parallel text bars print extra lines. Switch your import to from tqdm.auto import tqdm and install ipywidgets with %pip install ipywidgets, so tqdm uses the notebook widget instead of text.

What is the difference between tqdm.notebook and tqdm.auto?

from tqdm.notebook import tqdm always builds an ipywidgets bar: it raises the IProgress not found error when ipywidgets is missing and prints only one static line in a terminal. from tqdm.auto import tqdm uses the widget in notebook frontends and the text bar in plain Python or IPython terminals, QtConsole and Spyder, or when ipywidgets is missing.

Why is my bar showing in the wrong cell?

A notebook widget bar renders in the cell where the tqdm object was created, even if you call .update() on it in a cell further down. To make it appear in a different cell, create the bar with display=False and then call display(pbar.container) inside the cell where you want it to show.

How do I add a progress bar to pandas in a notebook?

Import tqdm from tqdm.auto, call tqdm.pandas(), and then use .progress_apply() instead of .apply() on your DataFrame or Series. Because you registered tqdm from tqdm.auto, pandas uses the ipywidgets bar in the notebook output.

Are tqdm.notebook widget bars streamed to Runpeek?

No. Runpeek streams standard tqdm text bars, and the widget bars from tqdm.notebook and from tqdm.auto inside a notebook are not sent. Use from tqdm_live import tqdm in the notebook instead; it shows the text bar in the cell and prints the browser link.

Check on long runs from your phone

Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.

Get a live linkpip install tqdm-live

Sources