Library / SDK
slowkow/ggrepel avatar
slowkow/ggrepel

ggrepel: the ggplot2 geoms that move overlapping text labels out of each other's way

:round_pushpin: Repel overlapping text labels away from each other in your ggplot2 figures.

1,262 stars95 forksRGPL-3.0

At a glance

What is it?
ggrepel is a small R package that adds two geoms to ggplot2, `geom_text_repel()` and `geom_label_repel()`, which push overlapping text labels away from each other, away from the data points and away from the panel edges. This looks at what it does, and at the case it cannot rescue.
Who is it for?
ggrepel fits a data visualiser who has more labels than room, who wants a legible scatterplot rather than a grid of text, and who is working in R with ggplot2 already. It does not fix a chart that is trying to say too much, because moving a label away from its point can turn a clear overlap into a misleading distance, and no amount of repelling reduces the number of things being said.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly R, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Overlapping labels are a data problem wearing a styling costume

ggrepel provides geoms for ggplot2 to repel overlapping text labels, and there are exactly two of them, `geom_text_repel()` and `geom_label_repel()`. The difference is the same difference as between `geom_text()` and `geom_label()` in ggplot2 itself, a plain text label against a label with a box behind it.

The rule the package applies is stated in one sentence in the readme, and it has three parts: text labels repel away from each other, away from data points, and away from the edges of the plotting area. That third clause is the one people forget, and it is the one that turns an unreadable mess into a legible figure, because a label pushed to the edge of the panel or clipped by it is worse than a label that overlaps.

The problem it solves is specific and extremely common. You have a scatterplot with a hundred points, you label every one, and the text is a black smear. The usual responses are to label fewer points, to shrink the font, or to accept the smear. ggrepel adds a fourth: keep all the labels and move them.

The audience is the working data visualiser, and the distribution is CRAN, with the badges in the readme pointing at the CRAN package page and the CRAN download logs. The project is licensed GPL-3.0, the language is R, and the site is ggrepel.slowkow.com.

Two geoms, and the example that shows the point

The readme's usage example is four lines of R, and it is worth reading because it shows that the geoms are drop-in replacements rather than a separate system.

r
library(ggrepel)
ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars))) +
  geom_text_repel() +
  geom_point(color = 'red') +
  theme_classic(base_size = 16)

You add the layer where you would have added `geom_text()`, the label aesthetic is the same, the scales are the same, and everything else in the plot is untouched. That is what makes it usable in an existing codebase of plots rather than only in a demonstration.

The animated image next to the example shows the repulsion resolving, which is the one behaviour of the package that cannot be seen in a static snapshot: the labels settle into a non-overlapping arrangement over a series of iterations. That is worth knowing about before you use it, because a plot that redraws on every parameter change has to run that settling each time.

The package also documents that it works with `stat_summary()`, with jittered points, with `sf` objects, and with multiline text where `hjust` controls the justification of the lines. Those are not exotic cases; they are the situations in which people discover that a text label is harder to place than it looks.

Install from CRAN, or from GitHub when you need the development version

Installation is the standard R pair, and the readme gives both.

r
# The easiest way to get ggrepel is to install it from CRAN:
install.packages("ggrepel")

# Or get the the development version from GitHub:
# install.packages("devtools")
devtools::install_github("slowkow/ggrepel")

The devtools route is commented in the readme, which tells you the intent: CRAN is the supported channel, and GitHub is for people who need a change that has not reached CRAN yet. Since the repository publishes no GitHub releases, the version on CRAN is the version to pin against for reproducible figures, and the CRAN badge in the readme is the one that reflects it.

The continuous integration is visible too, with a build status badge for the R-CMD-check workflow on GitHub Actions. R-CMD-check is the standard package validation, and a project that carries that badge is telling you that a change has been run through the same checks CRAN will apply, which is the thing that actually matters for a package you depend on in a figure-generating pipeline.

The examples page is where the options live

The readme is a list of thumbnail images, each one a link into the examples page, and the set of them is effectively the feature list. It covers hiding some of the labels, always showing all labels even when they have too many overlaps, not repelling labels from data points, not repelling labels from the plot or panel edges, expanding the scale to make room for labels, always or never drawing line segments, making curved line segments or arrows, repelling labels from data points with different sizes, limiting labels to a specific area, removing a variable from the legend, aligning labels on the top or bottom edge, aligning labels on the left or right edge, using ggrepel with `stat_summary()`, justifying multiple lines of text with `hjust`, labelling jittered points, nudging labels in different directions with the ggpp package, labelling sf objects, adding shadows or glow under text labels, and verbose timing information.

Read as a group, these describe a package that is not only about moving labels but about giving you control over the trade-off. Hiding some labels, always showing all of them, refusing to repel from points, and drawing the connector segment are all switches for the same underlying decision, which is how much of your data you are willing to distort for legibility.

Two of them deserve a note. The last one, verbose timing information, exists because the repulsion is an iterative computation and it can be slow on large numbers of labels, which is a performance characteristic the package is willing to expose rather than hide. And the shadow or glow under text labels is a legibility trick for labels that end up over a dense region of the plot, where a halo separates the text from what is behind it.

The examples are generated from `pkgdown/articles/examples.Rmd` and rendered by the build, so the figures in the readme and the code behind them cannot drift apart.

The repository is a full R package with a reproducible toolchain

The tree has everything an R package needs and a few things that go beyond it: `DESCRIPTION` and `NAMESPACE` at the root, `R/` for the R code, `src/` for the compiled parts, `man/` for the documentation, `tests/` for the test suite, `vignettes/` for the long-form articles, `docs/` for the built site, `pkgdown/` for the site configuration, and `build_site.R` and `index.md` for the site entry points. There is a `.lintr.R` for the linter configuration and a `ggrepel.Rproj` file for RStudio.

The `justfile` is the most interesting file in the repository, because it is a complete description of the maintainer's workflow in six recipes.

code
# Install the package
install:
  R -e 'devtools::install(build = TRUE, upgrade = "never")'

That install recipe passes `build = TRUE`, so the compiled code in `src/` is built rather than taken from a prebuilt binary, and `upgrade = "never"` means the recipe does not quietly move your other packages. The other recipes cover the examples, the manual pages through `devtools::document()`, the site through `pkgdown::build_site()`, the tests through `devtools::test()`, and a `check` recipe that stages a copy of the package into `_build` with a long rsync exclusion list before running `devtools::check()` against it.

That exclusion list is worth reading as a statement of what is not part of the package: the site, the documentation sources, the built examples, the PDF files, the RStudio project files, the compiled objects, the caches, and the repository's own tooling. It is what makes the check reflect what a user would actually install from CRAN, and it is a better practice than checking a working tree full of build products.

Where ggrepel is the wrong tool

Three limits are clear, and the first is the one to think about before you adopt it.

Repelling a label moves it away from the thing it describes, and a reader who does not look at the connector segment will misread the chart. The package mitigates this with the line segment options, always or never drawing them and making them curved or arrows, and that mitigation is the point: repelling without a connector is a way to make a wrong chart, and repelling with a connector is a way to make a busier chart. Neither is free, and the honest case for ggrepel is that a legible chart with connectors beats an illegible one without them.

The second is that it does not reduce the amount of data being said. If a hundred labels do not fit, they still do not fit, and the package's own examples acknowledge this with the option to hide some labels and the option to always show all of them even when they have too many overlaps. The right answer to too many labels is usually to label the interesting ones, facet the plot, or change the chart type, and ggrepel is the answer to the different problem of labels that fit but collide.

The third is scope. The package is two geoms, and it depends on being inside ggplot2, in R, with the plot built from layers. There is no standalone label placement library here, and the documentation points at the examples page rather than a manual, which is a reasonable choice for something this small but does mean the behaviour of each argument is learned by example.

On maintenance, the record is healthy for a package of this age. The last push to the repository was on 2026-10-09, the project publishes no GitHub releases because CRAN is the distribution channel, and the CRAN badge and the R-CMD-check workflow are the signals worth watching.

Editorial conclusion

ggrepel fits a data visualiser who has more labels than room, who wants a legible scatterplot rather than a grid of text, and who is working in R with ggplot2 already. It does not fix a chart that is trying to say too much, because moving a label away from its point can turn a clear overlap into a misleading distance, and no amount of repelling reduces the number of things being said. It is also the wrong tool if you are not in ggplot2, since the package is a pair of geoms and nothing else. Before you rely on it, read the examples page rather than the readme, because the options that matter, hiding labels, drawing connectors and expanding the scale, are all demonstrated there, and check that your labels still point at what they claim to point at once they have moved.

Frequently asked questions

What does ggrepel do?

It provides two ggplot2 geoms, `geom_text_repel()` and `geom_label_repel()`, which make text labels repel away from each other, away from data points, and away from the edges of the plotting area.

How do I install ggrepel?

From CRAN with `install.packages("ggrepel")`, or the development version from GitHub with `devtools::install_github("slowkow/ggrepel")` after installing devtools. The project publishes no GitHub releases, so the CRAN version is the one to pin for reproducible figures.

How do I keep the line segments that connect labels to their points?

The examples page covers the options for always or never drawing line segments and for making them curved or arrows, since a label that has been moved away from its point otherwise misreads as pointing somewhere else.

Does ggrepel work with stat_summary and jittered points?

Yes, there are dedicated examples for using ggrepel with stat_summary() and for labelling jittered points, along with examples for sf objects, for multiline text with hjust, and for nudging labels with the ggpp package.

What licence is ggrepel released under?

GPL-3.0, with the licence file at the repository root. The package is on CRAN and the project is built and tested with the R-CMD-check workflow, with the last push to the repository on 2026-10-09.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. Project website
  4. README
  5. slowkow/ggrepel on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/slowkow-ggrepel.svg)](https://hysenlabs.com/projects/slowkow-ggrepel)

Was this useful?