subplots
single legend
data visualization
matplotlib
plotting tips

How do I make a single legend for many subplots?

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

When several subplots share the same series labels, repeating the legend in every axis wastes space and makes the figure harder to read. In Matplotlib, the standard solution is to collect the legend handles once and attach a single legend to the figure rather than to each subplot.

The Figure-Level Legend Pattern

The cleanest approach is fig.legend(...). Plot the lines on the individual axes, get the handles and labels from one axis or from all axes, and then place one shared legend on the figure.

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4x = np.linspace(0, 10, 100)
5fig, axes = plt.subplots(2, 2, figsize=(8, 6), sharex=True, sharey=True)
6
7for ax in axes.flat:
8    ax.plot(x, np.sin(x), label="sin(x)")
9    ax.plot(x, np.cos(x), label="cos(x)")
10    ax.set_title("Example")
11
12handles, labels = axes[0, 0].get_legend_handles_labels()
13fig.legend(handles, labels, loc="upper center", ncol=2)
14fig.tight_layout(rect=[0, 0, 1, 0.92])
15plt.show()

Two lines matter most here:

  • 'fig.legend(...) creates the shared legend.'
  • 'tight_layout(rect=[...]) leaves room so the legend does not overlap the axes.'

Collecting Handles From Multiple Axes

If different subplots contain different plotted elements, collect handles and labels from all of them and deduplicate the labels.

python
1import matplotlib.pyplot as plt
2
3fig, axes = plt.subplots(1, 2, figsize=(8, 4))
4
5axes[0].plot([1, 2, 3], [2, 3, 5], label="sales")
6axes[0].plot([1, 2, 3], [1, 4, 4], label="cost")
7axes[1].plot([1, 2, 3], [5, 3, 2], label="sales")
8axes[1].scatter([1, 2, 3], [2, 2, 3], label="forecast")
9
10legend_map = {}
11for ax in axes:
12    handles, labels = ax.get_legend_handles_labels()
13    for handle, label in zip(handles, labels):
14        legend_map[label] = handle
15
16fig.legend(legend_map.values(), legend_map.keys(), loc="lower center", ncol=3)
17fig.tight_layout(rect=[0, 0.12, 1, 1])
18plt.show()

This avoids duplicate entries such as repeated sales lines.

Common Legend Placements

A shared legend is often placed:

  • above the subplots with loc="upper center"
  • below the subplots with loc="lower center"
  • outside the plotting area using bbox_to_anchor

For example, to place the legend outside on the right:

python
fig.legend(handles, labels, loc="center left", bbox_to_anchor=(1.02, 0.5))
fig.tight_layout(rect=[0, 0, 0.85, 1])

This is useful when the labels are long and horizontal space is limited.

Using constrained_layout

If you prefer not to manage the layout rectangle manually, constrained_layout=True can help.

python
fig, axes = plt.subplots(2, 1, figsize=(6, 6), constrained_layout=True)

It can reduce layout friction, but figure-level legends still sometimes need manual positioning. The most reliable approach is to place the legend intentionally and reserve space for it.

What Not to Do

A common anti-pattern is calling ax.legend() on every subplot and then trying to hide three of them later. That technically works, but it creates clutter and makes layout tuning harder.

Another weak approach is building the shared legend from only one axis when different axes contain different labels. If the content varies, gather handles from all subplots instead.

Common Pitfalls

The biggest mistake is forgetting that fig.legend needs handles and labels. If you never capture or retrieve them, the shared legend has nothing to show.

Another issue is placing the legend correctly but forgetting to reserve figure space. The result is a legend that overlaps titles or gets cut off in the saved image.

Duplicate labels are also common when multiple axes plot the same named series. Deduplicating before calling fig.legend keeps the legend compact.

Finally, make sure all intended plotted objects actually have labels. Matplotlib ignores unlabeled artists or names beginning with an underscore.

Summary

  • Use fig.legend(...) for one legend shared across many subplots.
  • Get handles and labels from one axis when all subplots share the same series.
  • Collect and deduplicate handles from all axes when subplot contents differ.
  • Reserve space with tight_layout(rect=[...]) or careful figure placement.
  • Prefer one clear figure-level legend over repeated axis-level legends.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.