Using Matplotlib’s constrained_layout to Eliminate Overlapping Labels
Learn how constrained_layout automatically adjusts subplot spacing so tick labels, titles, legends and colorbars never overlap, with a concrete example and notes on its limits.
13 Apr 2026, 22:12 UTC

Problem: Overlapping decorations when you add many subplots
When you create a figure with several subplots and each axis carries a long x‑label, a title, or a colorbar, the default layout often cuts off text or lets elements overlap. Manually calling plt.tight_layout() helps in simple cases, but with GridSpec, inset axes or multiple colorbars you end up fiddling with subplots_adjust values for each new plot.
Takeaway: Matplotlib’s constrained_layout feature automatically computes the spacing needed for tick labels, titles, legends and colorbars, so you can focus on the data instead of the layout.
How constrained_layout works
Setting constrained_layout=True tells Matplotlib to run a layout engine after the drawing pass. The engine measures the space required by every decorative element (tick labels, axis titles, figure titles, legends, colorbars) and adjusts the subplotpars (left, right, bottom, top, wspace, hspace) to make everything fit without overlap.
You can activate it in three equivalent ways:
- When creating the figure:
fig, ax = plt.subplots(constrained_layout=True) - After the figure exists:
fig.set_constrained_layout(True) - As a context manager (less common):
with plt.rc_context({'figure.constrained_layout.use': True}):
The feature works with the standard backends (TkAgg, Qt5Agg, macOSX) and with non‑interactive ones like Agg, PDF, and SVG, making it suitable for scripts that save figures to file.
Working with complex layouts
Constrained_layout shines when you combine it with:
- GridSpec: You can define a grid of varying row/height ratios and still get proper spacing.
- Inset axes: Small axes placed inside a parent axis are treated as another decorative element.
- Colorbars: Whether you add a colorbar via
fig.colorbarorplt.colorbar, the engine reserves space for it.
Because the layout computation is done after the artists are drawn, adding or removing any of these elements later will trigger a re‑adjustment the next time the figure is rendered.
Worked example: GridSpec with long labels and a colorbar
The following snippet creates a 2×2 GridSpec where the top row spans both columns. Each axis contains a long x‑label, a title, and the bottom‑right axis includes a colorbar. Constrained_layout is enabled at figure creation.
import matplotlib.pyplot as plt
import numpy as np
# Sample data
x = np.linspace(0, 2*np.pi, 400)
y = np.sin(x)
# Create figure with constrained_layout
fig = plt.figure(constrained_layout=True, figsize=(8, 6))
grid = fig.add_gridspec(2, 2, height_ratios=[1, 1.2], width_ratios=[1, 1], figure=fig)
# Top axis spanning both columns
ax_top = fig.add_subplot(grid[0, :])
ax_top.plot(x, y, 'C0')
ax_top.set_title('Sine wave – top spanning axis')
ax_top.set_xlabel('This is a very long x‑label that would normally overlap with the bottom row')
ax_top.set_ylabel('Amplitude')
# Bottom left
ax_bl = fig.add_subplot(grid[1, 0])
ax_bl.plot(x, np.cos(x), 'C1')
ax_bl.set_title('Cosine – bottom left')
ax_bl.set_xlabel('Angle (rad)')
ax_bl.set_ylabel('Value')
# Bottom right with colorbar
im = ax_br = fig.add_subplot(grid[1, 1])
# Dummy 2D data for colorbar
data = np.random.rand(20, 20)
im = ax_br.imshow(data, aspect='auto', cmap='viridis')
ax_br.set_title('Random image – bottom right')
ax_br.set_xlabel('Columns')
ax_br.set_ylabel('Rows')
# Add colorbar; constrained_layout will reserve space for it
cbar = fig.colorbar(im, ax=ax_br)
cbar.set_label('Intensity')
# Save the figure to inspect later
fig.savefig('constrained_layout_example.png', dpi=150)
plt.close(fig)
To verify that the layout succeeded, you can:
- Open
constrained_layout_example.pngand confirm that none of the titles, axis labels or the colorbar are clipped. - Run a quick Python check:
import matplotlib.pyplot as plt; fig = plt.figure(); fig.set_constrained_layout(True); print(fig.get_constrained_layout())which should outputTrue. - Compare the same code with
constrained_layout=Falseand note the overlapping labels in the saved image.
Trade‑offs and limitations
While convenient, constrained_layout is not a drop‑in replacement for tight_layout in every situation:
- Calling
tight_layout()after enabling constrained_layout can override the automatic adjustments and produce unexpected spacing. Use one or the other, not both. - Performance: The layout engine adds a small overhead proportional to the number of subplots. For typical figures (up to a few dozen axes) the impact is negligible. With hundreds of subplots you may notice a slower render time; in that case consider disabling constrained_layout and manually tuning
subplots_adjustfor the static layout. - Backend‑specific quirks: Some third‑party backends that tightly integrate with GUI event loops may ignore the constrained_layout hint. The default interactive backends and the raster/vector backends (Agg, PDF, SVG) respect it.
If you encounter unexpected clipping, first ensure that no tight_layout() call follows the constrained_layout activation, and then verify the figure’s DPI and size settings.
Actionable closing
For most day‑to‑day plotting tasks—especially when you are building figures with GridSpec, inset axes, or colorbars—start your figure with constrained_layout=True. It removes the need for manual tight_layout tweaks and keeps your code focused on the scientific content. Keep an eye on the performance warning only when you are deliberately creating very large numbers of subplots, and remember to avoid mixing constrained_layout with tight_layout.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.