Skip to main content
v2026.11,986 entries · CC-BY 4.0

How to Rotate X Axis Labels in ggplot2 (theme and guide_axis)

Rotate x axis labels in ggplot2 with element_text(angle) or guide_axis(angle), and use n.dodge or check.overlap when rotation is not the answer.

Written and maintained by CASRAI Editorial Board

Published · Last updated

On this page

To rotate x axis labels in ggplot2, add theme(axis.text.x = element_text(angle = 90)) to the plot, or use guides(x = guide_axis(angle = 90)). The second route is the one most tutorials skip: the ggplot2 reference says guide_axis() “also uses some heuristics” to pick the horizontal and vertical justification for you, which is the step that usually goes wrong when you rotate by hand. This guide shows both routes, then two fixes that avoid rotation altogether (n.dodge and check.overlap).

What it is used for in research

Categorical axes in research figures often carry long labels: treatment arms, site names, gene or sample identifiers, survey items. Horizontal labels collide, and journals and data-management plans expect figures to be legible without manual editing in a graphics program. Doing the rotation in code keeps the figure reproducible from the script, which fits the practice described in ggplots in R: build, style and save plots with ggplot2.

Install or access (official source only)

ggplot2 is an R package distributed through CRAN, the official R package repository. Install it once and load it in each session:

install.packages("ggplot2")
library(ggplot2)

The examples below use the mpg data set that ships with ggplot2, whose manufacturer column holds 15 text categories. The reference pages cited here are for ggplot2 4.0.3, checked on 2026-10-29.

Step-by-step: rotate, stagger or thin x axis labels

Start from a plain bar chart and add one fix at a time.

p <- ggplot(mpg, aes(manufacturer)) + geom_bar()

1. Rotate with theme() and element_text()

The label text on the x axis is controlled by the theme element axis.text.x, which the theme() reference lists among the “tick labels along axes” elements and which takes an element_text(). Set its angle argument:

p + theme(axis.text.x = element_text(angle = 90))

The element reference gives the allowed range for angle as 0 to 360 and the range for hjust and vjust as 0 to 1. A rotated label is anchored by those two settings, so add them and adjust until each label sits under its tick:

p + theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1))

The hjust and vjust values here are starting points to adjust for your own angle and font size, not fixed rules.

2. Rotate with guide_axis(angle)

The same result can be requested on the axis guide:

p + guides(x = guide_axis(angle = 90))

This form appears in the guide_axis() reference examples. Its documentation states that, compared with setting the angle in theme() or element_text(), it “also uses some heuristics” to automatically pick the hjust and vjust you probably want. If you only need a rotation and have no other styling reason to touch the theme, this is the shorter route.

3. Stagger labels with n.dodge

Instead of rotating, n.dodge spreads the labels over several rows. The reference defines it as the number of rows (for vertical axes) or columns (for horizontal axes) used to render the labels:

p + scale_x_discrete(guide = guide_axis(n.dodge = 2))

The reference documents the same argument on a continuous scale (scale_x_continuous(guide = guide_axis(n.dodge = 2))), so it works for numeric axes too. Staggered labels stay horizontal, which is easier to read than angled text when you have only a few crowded labels.

4. Thin labels with check.overlap

Setting check.overlap = TRUE makes ggplot2 “silently remove overlapping labels, (recursively) prioritizing the first, last, and middle labels”:

p + guides(x = guide_axis(check.overlap = TRUE))

Use this only when dropping some labels is acceptable for the figure. In a categorical chart where every bar must be identifiable it is the wrong choice, because the missing labels are not reported.

Output and how to read it

Each variant returns the same bars; only the label placement changes. Check three things before saving the figure: every category is still labelled (rules out check.overlap for categorical data), no label runs off the plot edge, and each rotated label ends at its own tick rather than between two ticks. If the labels look shifted, the cause is the justification, not the angle, which leads to the next section. For titles on the rotated axis see ggplot titles with labs(), and for the wider theme system see ggplot themes.

Troubleshooting

Why are my rotated labels not under the ticks?

Rotation with element_text(angle = ...) changes the text direction but leaves the justification at its defaults, so labels can look offset. Set hjust and vjust in the documented 0 to 1 range and adjust, or switch to guide_axis(angle = ...), which picks them heuristically.

Which theme element do I change?

The theme reference lists axis.text (all axes), axis.text.x (the x axis) and axis.text.x.top (an x axis drawn at the top). Setting axis.text.x rotates only the x axis; setting axis.text would affect the y axis as well.

Does the angle accept values outside 0 to 360?

The element reference documents the range as 0 to 360, so stay inside it.

My axis is on a log scale or needs breaks changed

Label rotation is independent of the scale. See ggplot log scale for transformed axes and axis in R for titles, limits, breaks and labels.

Free alternatives: rotate, stagger or thin

All four approaches below are part of ggplot2 itself, which is free open-source software distributed through CRAN.

Approach Code What the documentation says it does
Rotate via theme theme(axis.text.x = element_text(angle = 90)) angle accepts 0 to 360; hjust and vjust accept 0 to 1
Rotate via guide guides(x = guide_axis(angle = 90)) Uses heuristics to pick hjust and vjust automatically
Stagger guide_axis(n.dodge = 2) Sets the number of rows or columns used to render the labels
Thin guide_axis(check.overlap = TRUE) Silently removes overlapping labels, prioritizing the first, last and middle labels

FAQ

How do I rotate x axis labels 45 degrees in ggplot2?

Use theme(axis.text.x = element_text(angle = 45, hjust = 1)) and tune hjust and vjust until the labels sit under the ticks, or use guides(x = guide_axis(angle = 45)), since angle accepts a number of degrees.

What is the difference between element_text(angle) and guide_axis(angle)?

Both rotate the text. According to the ggplot2 reference, guide_axis(angle) additionally uses heuristics to choose hjust and vjust, whereas element_text() leaves those to you.

Can I avoid rotating the labels?

Yes. n.dodge staggers them over several rows or columns and check.overlap drops overlapping ones.

Sources (official docs)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about How to Rotate X Axis Labels in ggplot2 (theme and guide_axis)

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →