ggplot2 geom_text() in R: Add Text Labels to a Plot
The geom_text() function in ggplot2 places TEXT LABELS at specified (x, y) coordinates. Combined with hjust, vjust, and size aesthetics, it adds annotations to scatter plots and bar charts.
ggplot(df, aes(x, y, label = name)) + geom_text() geom_text(hjust = 0, vjust = -1) # offset positioning geom_text(size = 3, color = "red") geom_label() # boxed version ggrepel::geom_text_repel() # auto-adjust to avoid overlap
Need explanation? Read on for examples and pitfalls.
What geom_text() does in one sentence
geom_text() places text from the label aesthetic at each (x, y) coordinate. Width-zero text; place labels exactly at points.
Syntax
geom_text(mapping = NULL, data = NULL, hjust = 0.5, vjust = 0.5, size = 3.88, ...). Requires aes(x, y, label).
ggrepel::geom_text_repel() for plots with many labels. It automatically shifts overlapping labels apart, much cleaner than manual hjust/vjust.Five common patterns
1. Label scatter points
2. Numeric labels on bars
3. Different text per point
Empty string for unlabeled points.
4. ggrepel for non-overlapping
5. With size aesthetic
ggrepel::geom_text_repel is much better than manual offsets. It uses force-directed placement to avoid collisions.geom_text() vs geom_label() vs ggrepel
| Function | Style | Best for |
|---|---|---|
geom_text() |
Plain text | Lightweight labels |
geom_label() |
Boxed text | High-contrast labels |
ggrepel::geom_text_repel() |
Plain + auto-place | Many overlapping labels |
ggrepel::geom_label_repel() |
Boxed + auto-place | High-contrast non-overlap |
A practical workflow
Use ggrepel for any plot with more than a handful of labels.
Cleaner than manual hjust/vjust placement.
Common pitfalls
Pitfall 1: overlap. Many labels at similar coordinates overlap. ggrepel fixes this.
Pitfall 2: hjust/vjust confusion. hjust = 0 is left-aligned (label TO THE RIGHT of point); hjust = 1 is right-aligned (label TO THE LEFT). Test interactively.
geom_text() size is 3.88 (in mm). Often too small. Use size = 3 to 5 for typical plots.Try it yourself
Try it: Add car name labels to a scatter of wt vs mpg using ggrepel. Save to ex_plot.
Click to reveal solution
Explanation: ggrepel automatically places labels to avoid overlap.
Related ggplot2 functions
After mastering geom_text, look at:
geom_label(): boxed versionggrepel::geom_text_repel(): anti-overlapannotate("text", ...): one-off labelsgeom_text(check_overlap = TRUE): simple anti-overlap (less polished than ggrepel)
FAQ
What does geom_text do in ggplot2?
geom_text() places text from the label aesthetic at (x, y) coordinates.
What is the difference between geom_text and geom_label?
geom_text is plain text. geom_label adds a colored box behind the text. Use geom_label for high-contrast over busy plots.
How do I avoid label overlap?
Use ggrepel::geom_text_repel() (or geom_label_repel). They auto-shift labels to prevent collisions.
How do I position labels relative to points?
hjust/vjust: 0 is left/bottom-aligned; 1 is right/top-aligned. Negative values offset further. Test interactively.
Can I conditionally label points?
Yes. Pass an empty string for points to skip: mutate(label = ifelse(condition, name, "")).