How to put a legend outside a plot in Python
Learn how to move your Python plot legend outside the graph. This guide covers methods, tips, real-world uses, and common error debugging.

When you place a legend outside a plot in Python, you improve chart readability with complex data. Matplotlib offers flexible tools to position legends precisely and avoid obscured visual information.
You'll explore several techniques to position your legend for maximum clarity. You will find practical tips, see real-world applications, and get debugging advice to handle common placement issues.
Using bbox_to_anchor for a simple outside legend
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], label='Line 1')
plt.plot([3, 2, 1], label='Line 2')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()--OUTPUT--[Output: A plot with two lines and the legend placed outside to the right of the plot]
The bbox_to_anchor parameter gives you precise control for placing the legend outside the main plot area. It uses a coordinate system where (0, 0) is the bottom-left of the axes and (1, 1) is the top-right. By setting the x-coordinate to 1.05 in (1.05, 1), you push the legend's anchor point just beyond the plot's right boundary.
The loc='upper left' argument then tells Matplotlib to pin the legend's own top-left corner to that anchor point. This combination effectively positions the entire legend box to the right of your chart, preventing it from overlapping with your data.
Position-based legend placement
Beyond the precise coordinates of bbox_to_anchor, you can also manage legend placement with simpler methods like adjusting the loc parameter or using figlegend().
Using loc parameter with different positions
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], label='Line 1')
plt.plot([3, 2, 1], label='Line 2')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()--OUTPUT--[Output: A plot with the legend placed at the center-left position outside the plot area]
By pairing the loc parameter with bbox_to_anchor, you can precisely align your legend. The key is understanding that loc defines which part of the legend box gets attached to the coordinates you specify.
loc='center left'tells Matplotlib to use the vertical middle of the legend's left edge as the anchor point.bbox_to_anchor=(1, 0.5)then places this anchor point exactly at the right edge (x=1) and halfway up the vertical axis (y=0.5) of the plot.
This combination neatly centers the legend vertically next to the chart area.
Adjusting figure size to accommodate the legend
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot([1, 2, 3], label='Line 1')
ax.plot([3, 2, 1], label='Line 2')
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()--OUTPUT--[Output: A wider plot with more space for the legend on the right side]
If your external legend gets cut off, the figure itself might not be wide enough. You can solve this by making the entire plot canvas larger when you create it.
- The
figsizeparameter inplt.subplots()lets you define the figure’s width and height in inches. - Setting a larger width, like in
figsize=(8, 5), creates extra space on the canvas.
This gives plt.tight_layout() the room it needs to automatically resize the plot and fit the legend without cropping it.
Using the figlegend() function for figure-level legends
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
lines = [ax.plot([1, 2, 3], label=f'Line {i+1}')[0] for i in range(3)]
fig.legend(lines, [line.get_label() for line in lines],
loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()--OUTPUT--[Output: A plot with three lines and a figure-level legend placed outside]
Instead of attaching a legend to a single plot, figlegend() places it relative to the entire figure canvas. This approach is perfect when you have multiple subplots and need one shared legend for all of them.
Unlike an axes-specific legend, you must explicitly provide the plot objects and their labels:
- The code first captures the plotted line objects into a list called
lines. - It then extracts the text for each label using
line.get_label().
Finally, loc and bbox_to_anchor position the legend using the figure’s coordinate system.
Advanced layout techniques
For even greater control, you can move beyond manual placement by dedicating a subplot to the legend or using automated tools for polished, custom-styled results.
Creating a dedicated subplot for the legend
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(10, 5))
gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1])
ax1 = plt.subplot(gs[0])
ax2 = plt.subplot(gs[1])
lines = [ax1.plot(range(i, i+3), label=f'Line {i}')[0] for i in range(1, 4)]
ax2.axis('off')
ax2.legend(lines, [line.get_label() for line in lines], loc='center')
plt.tight_layout()
plt.show()--OUTPUT--[Output: A plot with a dedicated empty subplot on the right containing only the legend]
This method gives you precise layout control by creating a separate area just for the legend. You use matplotlib.gridspec.GridSpec to divide the figure into a grid—in this case, two columns with different widths.
- The
width_ratios=[3, 1]argument allocates more space to the plot and reserves a smaller column for the legend. - You then turn off the second subplot's axes with
ax2.axis('off'), making it an invisible container. - Finally, you place the legend inside this empty subplot, perfectly centering it with
loc='center'.
Using constrained_layout for automatic spacing
import matplotlib.pyplot as plt
fig, ax = plt.subplots(constrained_layout=True)
ax.plot([1, 2, 3], label='Line 1')
ax.plot([3, 2, 1], label='Line 2')
ax.plot([2, 3, 1], label='Line 3')
ax.legend(loc='center left', bbox_to_anchor=(1.05, 0.5))
plt.show()--OUTPUT--[Output: A plot with automatically adjusted spacing to accommodate the outside legend]
Matplotlib's constrained_layout offers a powerful, automated way to manage your plot's spacing. When you set constrained_layout=True in plt.subplots(), it intelligently resizes plot elements to prevent them from overlapping.
- It automatically makes room for the legend you placed outside the plot with
bbox_to_anchor. - Unlike
tight_layout(), you don't need to call it separately; it's activated when the figure is created.
This approach simplifies creating clean, well-spaced figures, especially when dealing with external legends or complex subplot arrangements. It handles the adjustments for you.
Creating a styled legend with fancybox and shadow
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], 'b-', label='Line')
ax.scatter([1, 2, 3], [3, 2, 1], color='r', label='Points')
handles, labels = ax.get_legend_handles_labels()
plt.figlegend(handles, labels, loc='center left', bbox_to_anchor=(1.05, 0.5),
fancybox=True, shadow=True)
plt.tight_layout()
plt.show()--OUTPUT--[Output: A plot with a fancy outside legend that has rounded corners and a shadow effect]
You can easily add visual polish to your legend for a more professional appearance. The code first gathers all plot elements and their labels using ax.get_legend_handles_labels(), which is a reliable way to manage multiple items. It then passes these to plt.figlegend() along with two simple styling arguments.
fancybox=Truegives the legend box rounded corners for a softer look.shadow=Trueadds a subtle drop shadow, helping the legend stand out from the plot background.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the visualization techniques we've explored, Replit Agent can turn them into production applications. It understands how to implement features like positioning a legend with bbox_to_anchor to keep your charts clean and readable.
- Build a financial dashboard that visualizes and compares multiple stock performances, using an external legend for clarity.
- Create a scientific data analysis tool that plots different experimental datasets on a single chart, with a shared legend managed by
figlegend(). - Deploy a server health monitor that tracks metrics like CPU and memory usage, ensuring the legend never obscures real-time data.
Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser.
Common errors and challenges
Placing a legend outside your plot can sometimes lead to frustrating errors, but most common issues have straightforward fixes.
Troubleshooting truncated legends with tight_layout()
A cut-off legend is a frequent problem, often happening when the figure canvas is too small. While plt.tight_layout() tries to adjust spacing, it can't create space that isn't there. If your legend is still clipped, it means the function ran out of room to work with.
- The simplest fix is to make the figure wider using the
figsizeparameter when you create it, likeplt.subplots(figsize=(10, 6)). This givestight_layout()the extra canvas space it needs. - For more granular control, you can use
plt.subplots_adjust(right=0.75)to manually shrink the plot area and free up space on the right side for the legend.
Fixing missing legend entries
If a line or point doesn't appear in your legend, it’s usually because it's missing a label. Matplotlib only includes elements that have a label attribute defined in their plotting function, such as ax.plot(x, y, label='My Line').
- Double-check that every element you want in the legend has a unique
label. - When using
figlegend()or creating a legend from multiple subplots, you must manually provide the plot objects (handles) and their corresponding labels. You can gather them reliably withax.get_legend_handles_labels()before passing them to the legend function.
Resolving legend overlap issues in multi-plot layouts
In figures with multiple subplots, a legend attached to one plot can easily overlap an adjacent one. This happens because an axes-level legend (created with ax.legend()) doesn't know about other subplots. The solution is to manage the legend at the figure level.
- Use
fig.legend()instead ofax.legend(). This positions the legend relative to the entire figure canvas, not just one subplot, preventing it from intruding on other plots. - For ultimate control, create a dedicated subplot for the legend using
GridSpec. This carves out a separate, reserved space where the legend can live without interfering with any data visualizations.
Troubleshooting truncated legends with tight_layout()
It’s a classic Matplotlib moment: your plot looks perfect, but the saved image has a clipped legend. Even when using plt.tight_layout(), the function can't create room if the figure's canvas is too small. The code below demonstrates this exact problem.
import matplotlib.pyplot as plt
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], label='Line 1')
plt.plot([3, 2, 1], label='Line 2')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
# Legend will be cut off in saved figure
plt.savefig('legend_plot.png')
The code positions the legend outside the plot with bbox_to_anchor. When you call plt.savefig(), it saves the figure as is, clipping anything that extends beyond the canvas boundaries because no layout adjustment was made to accommodate it.
The corrected code below shows how to ensure the entire figure, including the legend, is saved properly.
import matplotlib.pyplot as plt
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], label='Line 1')
plt.plot([3, 2, 1], label='Line 2')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.savefig('legend_plot.png', bbox_inches='tight')
The solution combines two functions to ensure your legend is fully visible in the saved file. While plt.tight_layout() helps adjust spacing, the crucial fix is adding bbox_inches='tight' to the plt.savefig() command. This argument forces Matplotlib to calculate a new bounding box that fits every element—including the external legend—before saving. Keep an eye out for this issue whenever you save plots with elements positioned outside the main axes.
Fixing missing legend entries
It's a common frustration when a line or point doesn't appear in your legend. This usually happens because Matplotlib only includes elements that have a label attribute defined. The code below demonstrates what happens when a plot is missing its label.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3]) # No label provided
scatter = ax.scatter([1, 2, 3], [3, 2, 1])
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
In the code, neither the ax.plot() call nor the ax.scatter() call includes a label argument. As a result, ax.legend() has no information to display. The corrected version below demonstrates the simple fix.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], label='Line')
scatter = ax.scatter([1, 2, 3], [3, 2, 1], label='Points')
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
The fix is to provide a label argument inside each plotting function, such as ax.plot(..., label='Line'). Matplotlib's ax.legend() function automatically gathers these labels to build the legend; without them, it has no information to display. This is a common oversight, especially when you're quickly visualizing data, so always double-check that every element you want in the legend has a defined label.
Resolving legend overlap issues in multi-plot layouts
In figures with multiple subplots, a legend from one plot can easily overlap an adjacent one. This happens because an axes-level legend, created with ax.legend(), doesn't know about other plots. The code below demonstrates this exact issue in a two-plot layout.
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.plot([1, 2, 3], label='Data 1')
ax2.plot([3, 2, 1], label='Data 2')
ax1.legend()
ax2.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout() # Legend will still overlap with right plot
The call to ax2.legend() positions the legend relative to the right plot's axes. This makes it unaware of the left plot, causing an overlap. The following code demonstrates the proper way to handle this.
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.plot([1, 2, 3], label='Data 1')
ax2.plot([3, 2, 1], label='Data 2')
ax1.legend()
ax2.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout(rect=[0, 0, 0.9, 1]) # Adjust right boundary
The fix is to manually reserve space using plt.tight_layout() with the rect parameter. The argument rect=[0, 0, 0.9, 1] instructs Matplotlib to confine its layout adjustments to the left 90% of the figure canvas. This carves out a dedicated margin on the right for the external legend, ensuring it doesn't overlap with other plots. You'll find this technique essential when working with crowded, multi-plot layouts.
Real-world applications
Putting these techniques into practice, you can build complex dashboards with a shared legend or create custom categorical data visualizations.
Creating a dashboard with multiple subplots sharing a single legend
When building a dashboard with multiple subplots, you can use a single, shared legend to keep the layout clean and avoid repeating information across your plots.
import matplotlib.pyplot as plt
import numpy as np
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6))
# Plot data on both subplots with the same styling
ax1.plot(np.arange(10), np.sin(np.arange(10)), 'b-', label='Sine')
ax1.plot(np.arange(10), np.cos(np.arange(10)), 'r-', label='Cosine')
ax2.plot(np.arange(10), np.sin(np.arange(10)), 'b-')
ax2.plot(np.arange(10), np.cos(np.arange(10)), 'r-')
# Add a single legend for the whole figure
fig.legend(loc='center right', bbox_to_anchor=(1.0, 0.5))
plt.tight_layout(rect=[0, 0, 0.85, 1])
plt.show()
This code creates a dashboard where two plots share one legend. You only need to define labels on the first plot, ax1, as fig.legend() automatically finds them to build a single, non-redundant legend for the entire figure.
- The
fig.legend()function places the legend relative to the whole figure, not just one plot. plt.tight_layout(rect=[0, 0, 0.85, 1])shrinks the plot area, creating a dedicated margin on the right so the legend doesn't overlap your data.
Building a custom legend for categorical data visualization
When your plot elements don't automatically carry labels, you can build a custom legend from scratch to clearly define what each color represents. This is common in categorical visualizations like bar charts, where you might use color to signify different groups but can't assign a label to each bar in the same way you would for a line. Instead, you'll need to create the legend items manually.
The technique involves creating legend “handles”—the visual markers like colored squares—yourself. You can use matplotlib.patches.Patch to generate a patch for each category, assigning it a color and a label. After creating a list of these custom patches, you simply pass them to the plt.legend() function to build a clean, descriptive legend for your plot.
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
categories = ['Low', 'Medium', 'High', 'Critical']
values = [15, 30, 45, 10]
colors = ['green', 'blue', 'orange', 'red']
plt.figure(figsize=(8, 5))
plt.bar(categories, values, color=colors)
# Create custom legend elements
legend_elements = [mpatches.Patch(facecolor=color, label=f'{cat} Priority')
for cat, color in zip(categories, colors)]
plt.legend(handles=legend_elements, title='Risk Levels',
bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()
This code manually constructs a legend for a bar chart, which is necessary because plt.bar doesn't automatically create one from a list of colors. You're essentially telling Matplotlib exactly what to display.
- A list of
mpatches.Patchobjects is created, where eachPatchis a colored square representing a category. - These patches are passed to
plt.legend()using thehandlesargument. - The legend is then given a title and positioned outside the plot using
bbox_to_anchorfor a clean layout.
Get started with Replit
Turn these techniques into a real tool. Tell Replit Agent: “Build a dashboard to compare stock prices with a shared legend” or “Create a tool to visualize server metrics with the legend outside the plot area.”
The agent writes the code, tests for errors, and deploys your app from a simple prompt. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


.png)
.png)