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
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
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
| Argument | Does what | Values |
|---|---|---|
fontsize | Size in points | 12, 'large' |
fontweight | Boldness | 'bold', 'normal' |
loc | Horizontal placement | 'left', 'center', 'right' |
pad | Gap above the axes, in points | 6 to 20 |
color | Text colour | Any 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']
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:
- Matplotlib tight_layout
- Matplotlib title font size
- Matplotlib set_xticks
- Matplotlib x-axis label
- Rotate tick labels 45 and 90 degrees
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.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn