Skip to content
Topics
Matplotlib
Matplotlib Legend Outside the Plot: bbox_to_anchor Cheatsheet

Matplotlib Legend Outside the Plot: bbox_to_anchor Cheatsheet

Published on

Updated on

Move a Matplotlib legend outside the axes with bbox_to_anchor and loc. Place it right, left, or below the plot, shrink axes when needed, and export with bbox_inches='tight' so nothing is clipped.

Default legends sit inside the axes and often cover data. To park a legend outside, pair loc (which corner of the legend box is the anchor) with bbox_to_anchor (where that anchor sits in axes coordinates).

Quick fix

import matplotlib.pyplot as plt
import numpy as np
 
x = np.linspace(0, 10, 200)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x), label="sin(x)")
ax.plot(x, np.cos(x), label="cos(x)")
 
# Outside, upper-right of the axes
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left", borderaxespad=0)
 
plt.tight_layout()
plt.show()
PlacementTypical call
Right of axeslegend(bbox_to_anchor=(1.02, 1), loc="upper left")
Right, vertically centeredlegend(loc="center left", bbox_to_anchor=(1.02, 0.5))
Below, horizontallegend(loc="upper center", bbox_to_anchor=(0.5, -0.12), ncol=n)
Abovelegend(loc="lower center", bbox_to_anchor=(0.5, 1.02), ncol=n)
Export without clippingsavefig(..., bbox_inches="tight") or tight_layout() / constrained_layout=True

Axes coordinates: (0, 0) is the lower-left of the axes, (1, 1) is the upper-right. Values slightly above 1 or below 0 sit just outside the plot frame.

The problem: legend over the data

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x, np.sin(x), label="sin(x)", lw=2)
ax.plot(x, np.cos(x), label="cos(x)", lw=2)
ax.legend(loc="best")  # still inside the axes
ax.set_title("Default legend (often overlaps data)")
plt.show()

Default Matplotlib legend inside the axes

loc="best" only chooses the least-bad inside corner. It never moves the legend into the figure margin.

How bbox_to_anchor + loc work together

  1. bbox_to_anchor=(x, y) — point in axes fraction coordinates (by default).
  2. loc="upper left" (etc.) — which point on the legend box is pinned to that anchor.
  3. borderaxespad — padding between the axes and the legend; set 0 when you want a tight park outside.

Mental model: anchor point on the plot + which corner of the legend sits on that point.

Place the legend to the right

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x, np.sin(x), label="sin(x)", lw=2)
ax.plot(x, np.cos(x), label="cos(x)", lw=2)
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left", borderaxespad=0)
ax.set_title("Legend outside: upper right of axes")
fig.tight_layout()
plt.show()

Legend placed outside to the right of the axes

If the window still clips the legend, call tight_layout() / use constrained_layout=True in subplots, or reserve space by shrinking the axes (next section).

Shrink the axes, then dock the legend

When you have many series, give the plot a dedicated legend column:

x = np.arange(10)
fig = plt.figure(figsize=(7.2, 4.2))
ax = fig.add_subplot(111)
 
for i in range(5):
    ax.plot(x, i * x, label=f"y = {i}x", lw=2)
 
# Leave ~22% of the figure width for the legend
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.78, box.height])
ax.legend(loc="center left", bbox_to_anchor=(1.02, 0.5), borderaxespad=0)
ax.set_title("Shrink axes, legend to the right")
plt.show()

Axes shrunk with legend docked on the right

Modern alternative: create the figure with layout engines that cooperate better with outside artists:

fig, ax = plt.subplots(layout="constrained")
# ... plot ...
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left")

Legend below the plot (horizontal)

Use ncol so entries sit in one row under the chart—good for slides and wide dashboards:

x = np.arange(10)
fig = plt.figure(figsize=(7.2, 4.4))
ax = fig.add_subplot(111)
 
for i in range(5):
    ax.plot(x, i * x, label=f"y = {i}x", lw=2)
 
box = ax.get_position()
ax.set_position([box.x0, box.y0 + box.height * 0.12, box.width, box.height * 0.88])
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.12), ncol=5)
ax.set_title("Horizontal legend below the plot")
plt.show()

Horizontal legend placed below the plot

Decision guide

SituationPrefer
2–4 series, simple line chartbbox_to_anchor=(1.02, 1), loc="upper left" + tight_layout()
Many series, need stable layoutShrink axes / layout="constrained" + right-side legend
Wide figure / presentationBottom legend with ncol
Map or image where any box hurtsSemi-transparent inside legend: framealpha=0.3 (compromise)
Saving PNG/PDF for papersAlways test bbox_inches="tight" (below)

Export trap: outside legend disappears in the file

Interactive windows can show the legend while savefig crops it. The usual fix:

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x, np.sin(x), label="sin(x)", lw=2)
ax.plot(x, np.cos(x), label="cos(x)", lw=2)
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left", borderaxespad=0)
ax.set_title("Outside legend kept with bbox_inches='tight'")
 
fig.savefig("legend-outside.png", dpi=150, bbox_inches="tight", facecolor="white")

Saved figure that keeps the outside legend via bbox_inches tight

Related deep dive: savefig cuts off labels. If labels still collide, bump figure size before micro-tuning anchors.

Extra controls (without moving the box)

ax.legend(
    bbox_to_anchor=(1.02, 1),
    loc="upper left",
    borderaxespad=0,
    frameon=True,
    fancybox=False,
    framealpha=0.95,
    fontsize=9,          # or prop={"size": 9}
    title="Series",
    alignment="left",
)
  • ncol — multi-column legend
  • framealpha — transparency when you must keep a legend over data
  • title — group label for multi-series charts

For general legend styling (not only position), see Matplotlib legend.

Common traps

TrapWhat you seeFix
Only set loc="right"Legend still insideNeed bbox_to_anchor beyond 1.0
Outside legend in GUI, missing in PNGCropped exportbbox_inches="tight" or constrained layout
bbox_to_anchor in data coords by mistakeLegend flies awayDefault is axes coords; pass a 2-tuple, not a Bbox unless intentional
Huge legend fontDominates figurefontsize=8–10, or fewer columns
Overlapping with colorbarSquashed marginReserve space for both, or put legend below

FAQ

Conclusion

Outside legends are a layout problem, not a mystery API: pin a legend corner with loc, place that pin with bbox_to_anchor just past the axes (> 1 or < 0), then make sure export uses tight/constrained layout so the margin survives in the file. Use a right-side stack for many series and a bottom ncol row for wide figures.

Related Guides