Legend Placement
Plot Design
Data Visualization
Graph Customization
Tutorial

How to put the legend outside the plot

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In matplotlib, the legend is placed inside the plot area by default, which can overlap with data. To move it outside, use the bbox_to_anchor parameter in plt.legend() to position the legend relative to the axes, and loc to set the anchor point. You typically also need to adjust the figure layout so the legend is not clipped. The same technique works with seaborn since it uses matplotlib under the hood.

Basic Legend Outside the Plot

python
1import matplotlib.pyplot as plt
2
3fig, ax = plt.subplots()
4
5ax.plot([1, 2, 3], [1, 4, 9], label="Quadratic")
6ax.plot([1, 2, 3], [1, 2, 3], label="Linear")
7ax.plot([1, 2, 3], [1, 8, 27], label="Cubic")
8
9# Place legend to the right of the plot
10ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
11
12plt.tight_layout()
13plt.show()

bbox_to_anchor=(1.05, 1) places the anchor point at x=1.05 (just outside the right edge) and y=1 (top). loc="upper left" aligns the upper-left corner of the legend box to that anchor point.

Common Positions

python
1# Right of the plot
2ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
3
4# Below the plot
5ax.legend(bbox_to_anchor=(0.5, -0.15), loc="upper center", ncol=3)
6
7# Above the plot
8ax.legend(bbox_to_anchor=(0.5, 1.15), loc="lower center", ncol=3)
9
10# Far right, vertically centered
11ax.legend(bbox_to_anchor=(1.05, 0.5), loc="center left")

The ncol parameter arranges legend entries in multiple columns, useful for horizontal legends below or above the plot.

Preventing Legend Clipping

When the legend is outside the axes, it may be clipped by the figure boundary:

python
1# Method 1: tight_layout (simplest)
2plt.tight_layout()
3
4# Method 2: subplots_adjust
5plt.subplots_adjust(right=0.75)  # Make room on the right
6
7# Method 3: constrained_layout (automatic)
8fig, ax = plt.subplots(constrained_layout=True)
9
10# Method 4: bbox_extra_artists in savefig
11fig.savefig("plot.png", bbox_extra_artists=[legend], bbox_inches="tight")

bbox_inches="tight" in savefig() automatically expands the figure boundary to include the legend.

With Subplots

python
1fig, axes = plt.subplots(1, 2, figsize=(12, 5))
2
3for ax in axes:
4    ax.plot([1, 2, 3], [1, 4, 9], label="Quadratic")
5    ax.plot([1, 2, 3], [1, 2, 3], label="Linear")
6
7# Single legend for all subplots
8handles, labels = axes[0].get_legend_handles_labels()
9fig.legend(handles, labels,
10           loc="lower center",
11           bbox_to_anchor=(0.5, -0.05),
12           ncol=2)
13
14plt.tight_layout()
15plt.show()

fig.legend() creates a figure-level legend instead of per-axes legends.

Seaborn Plots

Seaborn returns matplotlib axes, so the same technique works:

python
1import seaborn as sns
2
3tips = sns.load_dataset("tips")
4
5ax = sns.scatterplot(data=tips, x="total_bill", y="tip", hue="day")
6
7# Move the seaborn legend outside
8sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
9
10plt.tight_layout()
11plt.show()

sns.move_legend() (seaborn 0.12+) is the cleanest way. For older versions, use ax.legend(bbox_to_anchor=...).

Customizing Legend Appearance

python
1legend = ax.legend(
2    bbox_to_anchor=(1.05, 1),
3    loc="upper left",
4    borderaxespad=0,
5    frameon=True,              # Show border
6    framealpha=0.9,            # Background opacity
7    facecolor="white",         # Background color
8    edgecolor="gray",          # Border color
9    fontsize=10,
10    title="Categories",
11    title_fontsize=12,
12    ncol=1,
13    handlelength=2,            # Length of legend markers
14    handleheight=1,
15    columnspacing=1,
16)

Saving with the Legend Included

When saving to a file, the legend outside the axes is often cut off:

python
1fig, ax = plt.subplots()
2ax.plot([1, 2, 3], [1, 4, 9], label="Data")
3legend = ax.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
4
5# This INCLUDES the legend in the saved file
6fig.savefig("plot.png", bbox_inches="tight", dpi=150)
7
8# This may CLIP the legend
9fig.savefig("plot_clipped.png")  # Without bbox_inches="tight"

Always use bbox_inches="tight" when saving plots with external legends.

Common Pitfalls

  • Legend clipped in saved files: plt.savefig() without bbox_inches="tight" clips content outside the figure boundary. Always pass bbox_inches="tight" when the legend is outside the axes.
  • Confusing bbox_to_anchor coordinates: The values are in axes coordinates (0-1 is inside the axes). (1, 1) is the top-right corner of the axes. Values above 1 or below 0 are outside the axes.
  • loc misunderstanding: loc sets which part of the legend box aligns to the anchor. loc="upper left" with bbox_to_anchor=(1, 1) puts the legend's upper-left corner at the axes' top-right corner.
  • tight_layout conflicting with constrained_layout: Using both tight_layout() and constrained_layout=True together produces a warning and unpredictable results. Use one or the other.
  • Legend overlapping subplots: For multi-subplot figures, per-axes legends can overlap adjacent plots. Use a single fig.legend() positioned outside all subplots instead.

Summary

  • Use ax.legend(bbox_to_anchor=(x, y), loc="...") to position the legend outside the plot
  • Common positions: right (1.05, 1), below (0.5, -0.15), above (0.5, 1.15)
  • Use ncol for horizontal legend layouts below or above the plot
  • Always use bbox_inches="tight" in savefig() to prevent clipping
  • Use fig.legend() for a single legend shared across multiple subplots
  • Use sns.move_legend() for seaborn plots (seaborn 0.12+)

Course illustration
Course illustration

All Rights Reserved.