187

Is there a way to change the spacing between legend items in ggplot2? I currently have

legend.position ="top" 

which automatically produces a horizontal legend. However, the spacing of the items is very close together and I am wondering how to space them farther apart.

3
  • It would be useful to have a current solution to this now that opts is depreciated.
    – geotheory
    Commented Jun 3, 2014 at 9:30
  • Here: pastebin.com/NnxMiTeH
    – PatrickT
    Commented Dec 18, 2017 at 20:13
  • 3
    Tung's answer, currently at the bottom of this thread, has a July 2018 update. Bugs have been fixed and hacky workarounds like the one in the pastebin above are no longer needed.
    – PatrickT
    Commented Sep 27, 2018 at 15:50

9 Answers 9

177

ggplot2 v3.0.0 released in July 2018 has working options to modify legend.spacing.x, legend.spacing.y and legend.text.

Update Dec 2021 - to make legend.spacing.y work, you will need to set byrow = TRUE in the corresponding guide_legend. See also this thread. Example below.

Example: Increase horizontal spacing between legend keys

library(ggplot2)

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) + 
  geom_bar() +
  coord_flip() +
  scale_fill_brewer("Cyl", palette = "Dark2") +
  theme_minimal(base_size = 14) +
  theme(legend.position = 'top', 
        legend.spacing.x = unit(1.0, 'cm'))

Note: If you only want to expand the spacing to the right of the legend text, use stringr::str_pad()

Example: Increase vertical spacing (mind byrow = TRUE)

library(ggplot2)

ggplot(mtcars, aes(y = factor(cyl), fill = factor(cyl))) + 
  geom_bar() +
  theme(legend.spacing.y = unit(1.0, 'cm'))  +
  ## important additional element
  guides(fill = guide_legend(byrow = TRUE))

Example: Move the legend key labels to the bottom and increase vertical spacing

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) + 
  geom_bar() +
  coord_flip() +
  scale_fill_brewer("Cyl", palette = "Dark2") +
  theme_minimal(base_size = 14) +
  theme(legend.position = 'top', 
        legend.spacing.x = unit(1.0, 'cm'),
        legend.text = element_text(margin = margin(t = 10))) +
  guides(fill = guide_legend(title = "Cyl",
                             label.position = "bottom",
                             title.position = "left", title.vjust = 1)) 

Example: for scale_fill_xxx & guide_colorbar

ggplot(mtcars, aes(mpg, wt)) +
  geom_point(aes(fill = hp), pch = I(21), size = 5)+
  scale_fill_viridis_c(guide = FALSE) +
  theme_classic(base_size = 14) +
  theme(legend.position = 'top', 
        legend.spacing.x = unit(0.5, 'cm'),
        legend.text = element_text(margin = margin(t = 10))) +
  guides(fill = guide_colorbar(title = "HP",
                               label.position = "bottom",
                               title.position = "left", title.vjust = 1,
                               # draw border around the legend
                               frame.colour = "black",
                               barwidth = 15,
                               barheight = 1.5)) 


The below is obsolete, but is left for curious people.

For vertical legends, settinglegend.key.size only increases the size of the legend keys, not the vertical space between them

ggplot(mtcars) +
  aes(x = cyl, fill = factor(cyl)) +
  geom_bar() +
  scale_fill_brewer("Cyl", palette = "Dark2") +
  theme_minimal(base_size = 14) +
  theme(legend.key.size = unit(1, "cm"))

In order to increase the distance between legend keys, modification of the legend-draw.r function is needed. See this issue for more info

# function to increase vertical spacing between legend keys
# @clauswilke
draw_key_polygon3 <- function(data, params, size) {
  lwd <- min(data$size, min(size) / 4)
  
  grid::rectGrob(
    width = grid::unit(0.6, "npc"),
    height = grid::unit(0.6, "npc"),
    gp = grid::gpar(
      col = data$colour,
      fill = alpha(data$fill, data$alpha),
      lty = data$linetype,
      lwd = lwd * .pt,
      linejoin = "mitre"
    ))
}

### this step is not needed anymore per tjebo's comment below
### see also: https://ggplot2.tidyverse.org/reference/draw_key.html
# register new key drawing function, 
# the effect is global & persistent throughout the R session
# GeomBar$draw_key = draw_key_polygon3

ggplot(mtcars) +
  aes(x = cyl, fill = factor(cyl)) +
  geom_bar(key_glyph = "polygon3") +
  scale_fill_brewer("Cyl", palette = "Dark2") +
  theme_minimal(base_size = 14) +
  theme(legend.key = element_rect(color = NA, fill = NA),
        legend.key.size = unit(1.5, "cm")) +
  theme(legend.title.align = 0.5)

3
  • 4
    Your function to increase vertical spacing between legend keys is the only solution I've found that works exactly as I desire and with ease of use. Thanks!
    – Docconcoct
    Commented Jun 29, 2020 at 7:36
  • @tjebo which version is that? I'm using ggplot2 3.3.3 and when I try to use it like this geom_bar(..., key_glyph = "polygon3") I get an object 'draw_key_polygon3' of mode 'function' was not found error.
    – Anke
    Commented May 6, 2021 at 2:32
  • 1
    @Anke you still need to define the function as per Tungs answer
    – tjebo
    Commented May 6, 2021 at 6:33
85

I think the best option is to use guide_legend within guides:

p + guides(fill=guide_legend(
                 keywidth=0.1,
                 keyheight=0.1,
                 default.unit="inch")
      )

Note the use of default.unit , no need to load grid package.

2
  • 10
    This may work for horizontal legends. However, for my vertical legend to the right of the plot, this only increases the key' s height, not the spacing between keys. My legend keys are still very close to each other. Commented Jul 24, 2017 at 18:49
  • 12
    As Mushin says, this misses the point, as with other answers, if the legend is VERTICAL it stretches the legend keys (e.g. the line segments) without padding the space between keys.
    – PatrickT
    Commented Oct 6, 2017 at 10:09
52

To add spacing between entries in a legend, adjust the margins of the theme element legend.text.

To add 30pt of space to the right of each legend label (may be useful for a horizontal legend):

p + theme(legend.text = element_text(
    margin = margin(r = 30, unit = "pt")))

To add 30pt of space to the left of each legend label (may be useful for a vertical legend):

p + theme(legend.text = element_text(
    margin = margin(l = 30, unit = "pt")))

for a ggplot2 object p. The keywords are legend.text and margin.

[Note about edit: When this answer was first posted, there was a bug. The bug has now been fixed]

2
  • 1
    See also Tung's answer for a July 2018 update on these matters.
    – PatrickT
    Commented Sep 27, 2018 at 15:45
  • 5
    If you want to add space between items in a vertical legend on the bottom and top of each entry, try something like margin = margin(t = 5, b = 5, unit = "pt").
    – Nova
    Commented Aug 19, 2021 at 14:47
49

A simple fix that I use to add space in horizontal legends, simply add spaces in the labels (see extract below):

  scale_fill_manual(values=c("red","blue","white"),
                    labels=c("Label of category 1          ",
                             "Label of category 2          ",
                             "Label of category 3"))
3
  • 14
    This is the only answer so far which deals with the question! This might be helpful in case of many entries: scale_fill_manual(values=values, labels=setNames(paste(labels, " "), entries)).
    – SaschaH
    Commented Feb 16, 2016 at 12:43
  • 3
    Technically not very nice, especially when you have to introduce those spaces into factor levels, but it's the only working solution so for. Commented Dec 4, 2017 at 14:08
  • 2
    Or we can use str_pad to make life a little bit easier
    – Tung
    Commented Aug 8, 2018 at 16:25
41

Now that opts is deprecated in ggplot2 package, function theme should be used instead:

library(grid) # for unit()
... + theme(legend.key.height=unit(3,"line"))
... + theme(legend.key.width=unit(3,"line"))
2
  • 25
    This solution changes the height/width of the boxes as opposed to the spacing between them.
    – Berk U.
    Commented Jan 14, 2016 at 23:31
  • 1
    I needed to decrease the spacing between vertical legend items. Setting legend.spacing.y to a negative value didn't work but legend.key.size=unit(.8,"lines") did.
    – nisetama
    Commented Jan 8 at 5:44
31

Looks like the best approach (in 2018) is to use legend.key.size under the theme object. (e.g., see here).

#Set-up:
    library(ggplot2)
    library(gridExtra)

    gp <- ggplot(data = mtcars, aes(mpg, cyl, colour = factor(cyl))) +
        geom_point()

This is real easy if you are using theme_bw():

  gpbw <- gp + theme_bw()

#Change spacing size:

  g1bw <- gpbw + theme(legend.key.size = unit(0, 'lines'))
  g2bw <- gpbw + theme(legend.key.size = unit(1.5, 'lines'))
  g3bw <- gpbw + theme(legend.key.size = unit(3, 'lines'))

  grid.arrange(g1bw,g2bw,g3bw,nrow=3)

enter image description here

However, this doesn't work quite so well otherwise (e.g., if you need the grey background on your legend symbol):

  g1 <- gp + theme(legend.key.size = unit(0, 'lines'))
  g2 <- gp + theme(legend.key.size = unit(1.5, 'lines'))
  g3 <- gp + theme(legend.key.size = unit(3, 'lines'))

  grid.arrange(g1,g2,g3,nrow=3)

#Notice that the legend symbol squares get bigger (that's what legend.key.size does). 

#Let's [indirectly] "control" that, too:
  gp2 <- g3
  g4 <- gp2 + theme(legend.key = element_rect(size = 1))
  g5 <- gp2 + theme(legend.key = element_rect(size = 3))
  g6 <- gp2 + theme(legend.key = element_rect(size = 10))

  grid.arrange(g4,g5,g6,nrow=3)   #see picture below, left

Notice that white squares begin blocking legend title (and eventually the graph itself if we kept increasing the value).

  #This shows you why:
    gt <- gp2 + theme(legend.key = element_rect(size = 10,color = 'yellow' ))

enter image description here

I haven't quite found a work-around for fixing the above problem... Let me know in the comments if you have an idea, and I'll update accordingly!

  • I wonder if there is some way to re-layer things using $layers...
1
  • 1
    Make the legend.key transparent: theme(legend.key = element_rect(size = 30,color=alpha("transparent",0))) Commented Jun 12, 2019 at 18:36
17

From Koshke's work on ggplot2 and his blog (Koshke's blog)

... + theme(legend.key.height=unit(3,"line")) # Change 3 to X
... + theme(legend.key.width=unit(3,"line")) # Change 3 to X

Type theme_get() in the console to see other editable legend attributes.

3
  • 17
    Thanks for the suggestion and link to Koshke's blog! Unfortunately however this seems to change the size of the boxes but not the spacing between items. Commented Jul 6, 2012 at 18:53
  • You may be able to "fake" it with some type of overlaying grob. But I don't think theres a way to get extra spacing inside of the legend. This is the only mention of it I could find in ggplot2's mailing list: groups.google.com/forum/?fromgroups#!topic/ggplot2/PhkJpP8zJuM Commented Jul 9, 2012 at 0:33
  • 2
    I can use this to increase spacing between legends successfully. Using negative numbers helped decrease spacing between legends.
    – Nova
    Commented Dec 20, 2017 at 15:45
10

As of ggplot2 3.5.0, legend spacing has been reworked. For overview purposes, I'll repeat the graphic from the release blog.

enter image description here

To manipulate spacing between different legends, use the legend.spacing setting.

library(ggplot2)

p <- ggplot(mpg, aes(displ, hwy, colour = class, shape = factor(cyl))) +
  geom_point() +
  guides(colour = guide_legend(ncol = 2))

p + theme(
  legend.spacing = unit(1, "cm")
)

To control spacing of key-label pairs within legends, uses the legend.key.spacing settings. For historical reasons the y-spacing does not inherit from the main legend.key.spacing setting, so you'd have to explicitly state legend.key.spacing.y in that case.

p + theme(
  legend.key.spacing.x = unit(1, "cm"),
  legend.key.spacing.y = unit(1, "cm")
)

To manipulate the spacing between the labels and the keys, use the text margins.

p + theme(
  legend.text = element_text(margin = margin(l = 1, unit = "cm"))
)

Created on 2024-02-27 with reprex v2.1.0

0
6

Use any of these

legend.spacing = unit(1,"cm")
legend.spacing.x = unit(1,"cm")
legend.spacing.y = unit(1,"cm")
1
  • 2
    My issue with this is that the labels for the legends end up occupying the middle portion of the extra white space, as opposed to generating space between the colored boxes of the legend. I've found the answer offered here about padding under theme(legend.text) to be the most satisfying for this.
    – vashts85
    Commented Aug 25, 2020 at 21:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.