Automatically Position Non-Overlapping Text Labels with 'ggplot2'

Provides text and label geoms for 'ggplot2' that help to avoid overlapping text labels. Labels repel away from each other and away from the data points.

GitHub Version Build Status CRAN_Status_Badge CRAN_Downloads_Badge


ggrepel provides geoms for ggplot2 to repel overlapping text labels:

  • geom_text_repel()
  • geom_label_repel()

Text labels repel away from each other, away from data points, and away from edges of the plotting area.

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


# Or get the the development version from GitHub:
# install.packages("devtools")
# Or use the service


See the vignette for the code behind these examples:


Please submit an issue to report bugs or ask questions.

Please contribute bug fixes or new features with a pull request to this repository.

Related work

Academic Papers

An Efficient Algorithm for Scatter Chart Labeling

Sebastian Theophil, Arno Schödl

feature labeling problem. The goal is to position the largest number of point labels such that they do not intersect each other or their points. First we present an algorithm using a greedy algorithm with limited lookahead. We then present an algorithm that iteratively regroups labels, calling the first algorithm on each group, thereby identifying a close to optimal labeling order. The presented algorithm is being used in a commercial product to label charts, and our evaluation shows that it produces results far superior to those of other labeling algorithms.

This might be a good start for a revision of ggrepel.



A small library for automatically adjusting text position in matplotlib plots to minimize overlaps.

Ilya Flyamer's Python library that extends matplotlib.



An extensible framework for automatically placing direct labels onto multicolor 'lattice' or 'ggplot2' plots. Label positions are described using Positioning Methods which can be re-used across several different plots. There are heuristics for examining "trellis" and "ggplot" objects and inferring an appropriate Positioning Method.


Pretty word clouds.

The wordcloud package implements a spiraling algorithm to prevent text labels from overlapping each other.


Force field simulation of interaction of set of points. Very useful for placing text labels on graphs, such as scatterplots.

I found that functions in the FField package were not ideal for repelling overlapping rectangles, so I wrote my own.

See this gist for examples of how to use the wordcloud and FField packages with ggplot2.


ggrepel 0.8.0 2018-05-09

Bug fixes and improvements

  • Fix geom_label_repel(..., point.padding=NA). Reported by @mlell in issue 104.

  • See below for other changes made after ggrepel 0.7.0

ggrepel 0.7.3 2018-02-09


  • Add support for position parameter. See issue 69. This allows us to add text labels to points positioned with position_jitter(), position_dodge(), position_jitterdodge(), etc.

    Please note that this feature will not work with ggplot2 2.2.1 or older.

ggrepel 0.7.2 2018-01-14

FIXES (thanks to @AliciaSchep and @aphalo)

  • Fix warning about hjust. See issue 93.

  • Fix bug when subset of points is labeled in geom_label_repel. See issue 92.

ggrepel 0.7.1 2017-11-18

CHANGES (thanks to @AliciaSchep)

  • Add support for hjust and vjust parameters. See issue 69. Also see new examples in the vignette.

  • Add code to avoid intersecting line segments. See issue 34.

ggrepel 0.7.0 2017-09-28


  • Fix intersection between lines and rectangles, to reproduce the same aesthetically pleasant behavior as in version 0.6.5.

    This is an improvement on the sloppy implementation introduced in 0.6.8. See commit 28633d for more information.

ggrepel 0.6.12 2017-07-16


  • Reproduce identical plots by usign seed = 1 to set the seed in geom_text_repel() or geom_label_repel(). By default, no seed will be set.

    This is an improvement on the sloppy implementation introduced in 0.6.2. See issue 33 and issue 73 for more discussion of this feature. Thanks to Pierre Gramme for reminding me about this via email.

ggrepel 0.6.11 2017-07-08

CHANGES (thanks to @seaaan)

  • Allow certain parameters to be passed as numbers or unit() instead of only units. See issue 79.

ggrepel 0.6.10 2017-03-07

FIXES (thanks to @zkamvar)

  • Fix the crash for plots that do not specify xlim or ylim. See pull 74.

ggrepel 0.6.9 2017-03-07

FIXES (thanks to @pcroteau)

  • Fix the crash for plots with facet_wrap or facet_grid that have no labeled points. See pull 70.

ggrepel 0.6.8 2017-02-12

NEW FEATURE (thanks to @AliciaSchep)

  • Constrain repulsion force to x-axis "x" or y-axis "y" with direction in geom_text_repel() and geom_label_repel(). See pull 68.

ggrepel 0.6.7 2017-01-09

CHANGES (thanks to @lukauskas)

  • Constrain text labels to specific areas of the plot with xlim and ylim in geom_text_repel() and geom_label_repel(). See pull 67.

ggrepel 0.6.6 2016-11-28

FIXES (thanks to @fawda123)

  • Mathematical expressions as labels with parse = TRUE in geom_text_repel() and geom_label_repel(). See issue 60.

ggrepel 0.6.5 2016-11-22

CHANGES (thanks to @jiho)

  • changed alpha in geom_label_repel() to control text, label background, label border, and segment.

  • Allow segment.colour as well as segment.color.

  • By default, map text color and text alpha to the segment color unless they are overridden.

FIXES (thanks to @jiho)

  • Call scales::alpha() instead of alpha().

ggrepel 0.6.4 2016-11-08


  • Fix a bug that caused ggrepel to fail on polar coordinates coord_polar(). See issue 56.

ggrepel 0.6.3 2016-10-14


  • Use point.padding=NA to ignore data points in repulsion calculations.

ggrepel 0.6.2 2016-10-06


  • Stop the labels from escaping the plot boundaries instead of applying a force at the boundary.

  • Call set.seed within geom_text_repel() and geom_label_repel() to allow recreating identical plots. Fixes issue 33.


  • Add min.segment.length to geom_text_repel() and geom_label_repel().

ggrepel 0.6.1 2016-10-04


  • Tweak repel_boxes.cpp. Dampen forces to tune how the labels move. The result looks better, at least for the examples in the vignette.

ggrepel 0.6.0 2016-10-03


  • Do not draw labels with empty strings. When a label is an empty string, the text will not be shown, the segment will not be drawn, but the corresponding data point will repel other labels. See issue 51.
  • Add segment.alpha as an option for geom_text_repel() and geom_label_repel().

  • Implement angle aesthetic for geom_text_repel(), the same way as done in ggplot2 geom_text().


  • Move nudge_x and nudge_y out of the aesthetics function aes(). This makes ggrepel consistent with ggplot2 functions geom_text() and geom_label(). Backwards incompatible with 0.5.1.

  • Restore segment.color as an option for geom_text_repel() and geom_label_repel().

  • Tweak repel_boxes.cpp. Do not weight repulsion force by ratios of bounding box heights and widths. This seems to perform better, especially after rotating text labels.

ggrepel 0.5.1 2016-02-22

  • Optimize C++ code further by reducing number of calls to rnorm().

ggrepel 0.5 2016-02-08

  • First push to CRAN.

ggrepel 0.4.6 2016-02-07


  • Tweak point.padding so that users can configure how far labels are pushed away from data points.

ggrepel 0.4.5 2016-02-06


  • Optimize C++ code for a 2.5X speed improvment.

  • Delete unnecessary .Rd files.

ggrepel 0.4.4 2016-02-05


  • Fix the bug when the line segment from the data point points to the origin at (0,0) instead of the text label.


  • Automatically recompute repulsion between labels after resizing the plot.

ggrepel 0.4.3 2016-01-18


  • Change distance between segment and label in geom_label_repel(). Now there is no gap between the end of the segment and the label border.

ggrepel 0.4.2 2016-01-15


  • Fix spring_force() so that it never returns NaN.


  • Add nudge_x and nudge_y to better control positioning of labels.

ggrepel 0.4.1 2016-01-13


  • Add arrow parameter to allow plotting arrows that point to the labeled data points rather than plain line segments.

  • Always draw segments, even if the labeled point is very close to the label.


  • Fix point.padding so that horizontal and vertical padding is calculated correctly.

  • Tweak forces to improve layout near borders and in crowded areas.

ggrepel 0.4 2016-01-12


  • Fix issue 7. Labels can now be placed anywhere in the plotting area instead of being limited to the x and y ranges of their corresponding data points.
  • Fix DESCRIPTION to require ggplot2 >= 2.0.0


  • Add new parameter point.padding to add padding around the labeled points. The line segment will stop before reaching the coordinates of the point. The text labels are also now padded from the line segment to improve legibility.

  • Add volcano plot to the vignette usage examples.

  • Add Travis continuous integration to test against R-devel, R-release, and R-oldrel.

  • Dampen repulsion force to slightly improve algorithm efficiency.

  • Move intersect_line_rectangle() to src/repel_boxes.cpp.

ggrepel 0.3 2016-01-08


  • Remove unused imports: colorspace.

  • Update NAMESPACE with new version of roxygen.

  • Use spring force to attract each label to its own point.

  • Change default maximum iterations from 10,000 to 2000.

  • Update man pages.

  • Remove unused code.

ggrepel 0.2.0 2016-01-07


  • Update geom_text_repel() and geom_label_repel().

    • Change label.padding to box.padding.

    • Remove unsupported parameters:

      • position
      • nudge_x
      • nudge_y
      • hjust
      • vjust
  • Remove unused imports.


  • Add roxygen docs to all functions.

ggrepel 0.1.0 2016-01-05


  • Add geom_label_repel().

  • Add fudge width to help with legends.

  • Add expand=TRUE to allow text to be placed in the expanded plot area.

  • Add man/ folder.

  • Add links to ggplot2 docs in vignette.


  • Add unused R implementation of repel_boxes(), just for your reference.

ggrepel 0.0.1 2016-01-04

  • Initial release to github.

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.


0.9.1 by Kamil Slowikowski, a year ago

Report a bug at

Browse source code at

Authors: Kamil Slowikowski [aut, cre] , Alicia Schep [ctb] , Sean Hughes [ctb] , Trung Kien Dang [ctb] , Saulius Lukauskas [ctb] , Jean-Olivier Irisson [ctb] , Zhian N Kamvar [ctb] , Thompson Ryan [ctb] , Dervieux Christophe [ctb] , Yutani Hiroaki [ctb] , Pierre Gramme [ctb] , Amir Masoud Abdol [ctb] , Malcolm Barrett [ctb] , Robrecht Cannoodt [ctb] , Michał Krassowski [ctb] , Michael Chirico [ctb] , Pedro Aphalo [ctb]

Documentation:   PDF Manual  

GPL-3 | file LICENSE license

Imports grid, Rcpp, rlang, scales

Depends on ggplot2

Suggests knitr, rmarkdown, testthat, gridExtra, devtools, prettydoc, ggbeeswarm, dplyr, magrittr, readr, stringr

Linking to Rcpp

Imported by AgroR, BasketballAnalyzeR, CAinterprTools, CGPfunctions, CLONETv2, ChemoSpecUtils, CoNI, D2MCS, EIX, FactoMineR, Factoshiny, FunnelPlotR, GDAtools, GOCompare, GUniFrac, GmAMisc, HLMdiag, LSX, MBAnalysis, MCAvariants, MEGENA, MicrobiomeStat, NACHO, NetworkChange, NetworkInference, OVtool, PAC, RamanMP, Riemann, SWTools, SensoMineR, Seurat, Signac, SparseBiplots, StabilizedRegression, SubgrPlots, Xplortext, affinitymatrix, amanida, auditor, autostsm, bbsBayes, bibliometrix, card, cinaR, clustrd, clustree, conos, conquestr, conserveR, correlationfunnel, corrr, dampack, densityClust, discourseGT, dmbc, dtwclust, dyngen, dynplot, eat, factoextra, ferrn, fgeo.plot, flightplot, gMOIP, gWQS, geofacet, germinationmetrics, gfer, ggdag, ggdemetra, ggfacto, gghalfnorm, gghighlight, ggmosaic, ggnetwork, ggpubr, ggquickeda, ggraph, ggspectra, ggstatsplot, gwaRs, gwasforest, healthyR,, iCellR, iNZightRegression, idm, immunarch, inTextSummaryTable, influenceAUC, isobxr, keyATM, kindisperse, mFD, manhplot, matuR, metan, metaprotr, mixpoissonreg, mizer, mosaic, o2plsda, owidR, palaeoSig, plinkQC, pmxTools, pooling, processR, protti, qtl2ggplot, quantable, quanteda.textplots, r2dii.plot, r4lineups, rStrava, radiant.model, radiant.multivariate, randomForestExplainer, rliger, rnmamod, rosetta, sNPLS, sccore, sensitivityCalibration, skewlmm, skynet,, spotoroo, statVisual, statgenSTA, stminsights, suddengains, swfscMisc, symphony, tetraclasse, text, tidyMicro, tidyestimate, ufs, visae, vistime, visualpred, visvow, vlda, volcano3D, wilson, wpa, yaps.

Depended on by CA3variants, CAvariants, FactoClass, SetMethods, func2vis.

Suggested by AlleleShift, BiplotML, CVEK, Census2016, DALEXtra, DataVisualizations, Guerry, MSEtool, NHSRdatasets, Platypus, RxODE, TextMiningGUI, UCSCXenaShiny, ZIprop, avocado, brainGraph, cfbfastR, circumplex, coveffectsplot, covid19br, dartR, dimensio, echor, ecocomDP, fairmodels, geofi, ggalluvial, ggfocus, ggfortify, ggiraph, ggparliament, ggpmisc, ggpp, ggspatial, ggwordcloud, grattan, hoopR, httk, ibawds, konfound, lcars, microeco, nCov2019, opticskxi, pixarfilms, precisely, psycModel, see, sigminer, signs, sjPlot, textplot, threesixtygiving, tidybayes, tidygeocoder, topicmodels.etm, ubiquity, usmap, wehoop.

See at CRAN