Matplotlib Subplot Titles and the Figure suptitle

A Matplotlib subplot title comes from ax.set_title(), and the title over the whole figure comes from fig.suptitle(). They’re different objects on different owners:

ax.set_title("Chairs")            # one axes
fig.suptitle("Sales by product")  # the whole figure

Use both together and they collide, which is the reason most people end up searching for this twice. The fix is one line and it’s further down.

Figures below come from Matplotlib 3.11.2 on Python 3.12.5.

Setting a title on each subplot

Loop over the axes and give each one its own title. You set the figure title once, on the figure itself:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 3, figsize=(10, 3))

for ax, name in zip(axes, ["Chairs", "Tables", "Desks"]):
    ax.bar(["Q1", "Q2", "Q3"], [4, 7, 5])
    ax.set_title(name)                       # one title per axes

fig.suptitle("Sales by product")             # one title for the whole figure

print("axes titles:", [ax.get_title() for ax in axes])
print("figure title:", fig._suptitle.get_text())
plt.close(fig)

Output:

axes titles: ['Chairs', 'Tables', 'Desks']
figure title: Sales by product
A Matplotlib figure with four bar chart subplots, each with its own title, under one bold overall figure title
Four subplot titles from set_title, one suptitle above them.

axes from plt.subplots is a NumPy array, so axes.flat is the tidiest way to walk a grid of any shape.

Why does plt.title only label one subplot?

plt.title() acts on whichever axes is current, which after plt.subplots is the last one created:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 3, figsize=(9, 2.6))
for ax in axes:
    ax.plot([1, 2, 3], [2, 4, 3])

plt.title("only the last one")        # plt.title hits the CURRENT axes

print("titles:", [repr(ax.get_title()) for ax in axes])
print("current axes is the last:", plt.gca() is axes[-1])
plt.close(fig)

Output:

titles: ["''", "''", "'only the last one'"]
current axes is the last: True
Command Prompt showing that plt.title set a title on only the last of three Matplotlib subplots
Two empty titles and one set: plt.title only reached the current axes.

That’s the single most common subplot titling bug. Use ax.set_title() on the axes you mean, the same discipline that set_xticks needs.

Styling a subplot title

set_title takes any Text property, plus loc and pad:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(5, 3))
ax.plot([1, 2, 3], [2, 4, 3])

# set_title returns the Text object, which is the reliable way to read it back:
# with loc="left" the left title is a SEPARATE object from ax.title
title = ax.set_title("Revenue", fontsize=15, fontweight="bold",
                     color="#0b6bcb", loc="left", pad=14)

print("ax.title (centre) :", repr(ax.title.get_text()), "<- empty, loc='left' used another slot")
print("text    :", title.get_text())
print("size    :", title.get_fontsize())
print("weight  :", title.get_fontweight())
print("position:", round(title.get_position()[0], 2), "<- 0.0 is left, 0.5 centre")
plt.close(fig)

Output:

ax.title (centre) : '' <- empty, loc='left' used another slot
text    : Revenue
size    : 15.0
weight  : bold
position: 0.0 <- 0.0 is left, 0.5 centre
ArgumentDoes whatValues
fontsizeSize in points12, 'large'
fontweightBoldness'bold', 'normal'
locHorizontal placement'left', 'center', 'right'
padGap above the axes, in points6 to 20
colorText colourAny Matplotlib colour

loc="left" is worth knowing, and it has a catch you can see in the output above. Matplotlib keeps left, centre and right titles as three separate Text objects, so ax.title stays empty when you use loc.

Read it back with ax.get_title(loc="left"), or just keep what set_title hands you. Left-aligned titles line up with your y axis instead of floating over the middle.

Stopping suptitle overlapping the subplot titles

By default the figure title is placed near the top of the figure, which is exactly where the first row of subplot titles already is:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(7, 5))
for ax in axes.flat:
    ax.plot([1, 2, 3], [2, 4, 3])
    ax.set_title("subplot title")

fig.suptitle("Everything at once", fontsize=16)

# without this the suptitle sits on top of the first row of subplot titles
fig.tight_layout()

print("suptitle y position:", round(fig._suptitle.get_position()[1], 3))
print("tight_layout leaves room automatically")
plt.close(fig)

Output:

suptitle y position: 0.98
tight_layout leaves room automatically

fig.tight_layout() recalculates the spacing and makes room for both. It’s the one-line fix and it handles most grids.

When you need finer control, fig.subplots_adjust(top=0.9) sets the space explicitly, or pass y= to suptitle to move it. The same tight_layout rules apply as anywhere else.

Row and column titles across a subplot grid

For a grid where every column is a region and every row a year, per-subplot titles get repetitive. Title the top row and label the first column instead:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 3, figsize=(9, 5))

for ax in axes.flat:
    ax.plot([1, 2, 3], [2, 4, 3])
    ax.label_outer()                       # hide the inner tick labels

# column headings on the top row only
for ax, name in zip(axes[0], ["North", "South", "East"]):
    ax.set_title(name)

# row headings via the y label of the first column
for ax, name in zip(axes[:, 0], ["2025", "2026"]):
    ax.set_ylabel(name, rotation=0, labelpad=24, fontweight="bold")

fig.suptitle("Sales by region and year", fontsize=15)
fig.tight_layout()

print("column titles:", [ax.get_title() for ax in axes[0]])
print("row labels   :", [ax.get_ylabel() for ax in axes[:, 0]])
plt.close(fig)

Output:

column titles: ['North', 'South', 'East']
row labels   : ['2025', '2026']
Command Prompt showing the column titles on the top row and the row labels on the first column of a Matplotlib subplot grid
Three column titles, two row labels, and one figure title over the lot.

ax.label_outer() then hides the tick labels on the inner axes, so the grid reads as one table rather than six separate charts.

More Matplotlib layout guides:

Frequently asked questions

How do I add a title to each subplot in Matplotlib?

Call ax.set_title() on each axes, usually in a loop over axes.flat. The arguments are in the set_title reference.

How do I add one title for all subplots?

fig.suptitle("text"). It belongs to the figure, not to any axes, so it sits above the whole grid.

Why does plt.title only set one subplot title?

Because it applies to the current axes, which is the last one created. Use ax.set_title() on each axes instead.

Why does my suptitle overlap the subplot titles?

The default position sits where the top row’s titles are. Call fig.tight_layout(), or set fig.subplots_adjust(top=0.9).

How do I left-align a subplot title?

ax.set_title("text", loc="left"). It lines the title up with the y axis rather than centring it.

How do I change the subplot title font size?

Pass fontsize to set_title, or set plt.rcParams['axes.titlesize'] once for every plot.

How do I add titles to rows and columns of a grid?

Use set_title on the top row for columns and set_ylabel on the first column for rows, then one suptitle for the figure.

Leave a Comment