ggplot2 element_text() in R: Style Every Text Element

The element_text() function in ggplot2 styles every text element on a plot, titles, axis labels, legends, facet strips, and captions. You pass it inside theme() with arguments like size, face, color, family, angle, hjust, and vjust.

⚡ Quick Answer
element_text(size = 14)                                # bigger text
element_text(face = "bold")                            # bold title
element_text(color = "navy")                           # color text
element_text(family = "serif")                         # change font family
element_text(angle = 45, hjust = 1)                    # rotate axis labels
element_text(margin = margin(t = 10))                  # add top margin
element_text(size = 12, face = "italic", color = "grey40") # combine styles

Need explanation? Read on for examples and pitfalls.

📊 Is element_text() the right tool?
STARTstyle a text element inside theme()element_text(size = 14, face = "bold")style a line or gridelement_line(color = "black")style a filled background or borderelement_rect(fill = "white")hide an element entirelyelement_blank()change the words shown (not the look)labs(title = "...", x = "...")pick a different built-in looktheme_minimal(), theme_classic()

What element_text() does in ggplot2

element_text() builds a text-styling specification that ggplot2 plugs into any text slot. It does not render anything by itself. You always pass the result to theme() as the value of a text argument, like plot.title = element_text(size = 16).

Every text element in a ggplot2 plot has a name: plot.title, plot.subtitle, plot.caption, axis.title, axis.text, legend.title, legend.text, strip.text. Each one accepts an element_text() value. The helper makes styling reusable, you write one element_text(size = 14) and apply it everywhere.

Theme arguments inherit from broader categories. Setting text = element_text(family = "Arial") propagates the font to every other text element. More specific settings, like axis.text, override the broader ones.

Key Insight
element_text() is a specification, not a styled string. ggplot2 stores the spec, then applies it when rendering. This is why you can swap fonts across 50 plots by changing one base theme.

Syntax and arguments

element_text(family, face, color, size, hjust, vjust, angle, lineheight, margin) covers every styling need. Each argument has a sensible default, so you only specify what you want to change.

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RLoad ggplot2 and create a base plot
library(ggplot2) p <- ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) + geom_point(size = 3) + labs(title = "Fuel Economy by Weight", subtitle = "mtcars dataset, 32 cars", x = "Weight (1000 lbs)", y = "Miles per gallon", color = "Cylinders", caption = "Source: 1974 Motor Trend") p

  

The default theme renders all text in a moderate grey sans-serif. Every element you see, title, subtitle, axis labels, caption, legend, can be restyled with one element_text() per slot.

Argument Type Common values
family character "sans", "serif", "mono", "Arial", "Times"
face character "plain", "bold", "italic", "bold.italic"
color character named colors, hex codes, "grey40"
size numeric 8 to 24 (points)
hjust numeric 0 (left), 0.5 (center), 1 (right)
vjust numeric 0 (bottom), 0.5 (middle), 1 (top)
angle numeric 0 to 360 degrees
margin margin() margin(t, r, b, l) in points

Seven styling patterns with element_text()

1. Bold, larger title

RLarger bold title in navy
p + theme( plot.title = element_text(size = 18, face = "bold", color = "navy") )

  

size is in points. face accepts "plain", "bold", "italic", or "bold.italic". Use these to set hierarchy across title, subtitle, and axis text.

2. Rotate x-axis labels

RAngle x-axis labels 45 degrees
ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot() + theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1))

  

angle rotates around the label's center by default. Add hjust = 1 so the right edge aligns with the tick, otherwise labels overlap the axis line.

Tip
Set both hjust and vjust when rotating. hjust = 1 aligns the right edge; vjust = 1 aligns the top edge. Together they keep rotated labels visually anchored.

3. Center the title

RCenter-align the plot title
p + theme( plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5) )

  

hjust accepts 0 (left), 0.5 (center), 1 (right). The default for plot.title is 0 (left), which is why centering needs an explicit override.

4. Custom font family

RApply a serif font to all text
p + theme( text = element_text(family = "serif"), plot.title = element_text(face = "bold", size = 16) )

  

Setting text = element_text(...) cascades to every text element. Individual settings still override the cascade. To use Google Fonts or system fonts, register them first via showtext::font_add_google() or extrafont::font_import().

5. Style the caption and subtitle

RSubdued grey caption, italic subtitle
p + theme( plot.subtitle = element_text(size = 11, color = "grey30", face = "italic"), plot.caption = element_text(size = 9, color = "grey50", hjust = 0) )

  

The default caption is right-aligned (hjust = 1). Set hjust = 0 to left-align it under the axis label.

6. Margin around text

RAdd space below the title
p + theme( plot.title = element_text(size = 16, face = "bold", margin = margin(t = 0, r = 0, b = 10, l = 0)) )

  

margin(t, r, b, l) is in points. Use it when titles crowd the panel or axis labels collide with tick marks.

7. Style facet strip text

RBold white text on a dark facet strip
ggplot(mtcars, aes(wt, mpg)) + geom_point() + facet_wrap(~ cyl) + theme( strip.text = element_text(color = "white", face = "bold", size = 12), strip.background = element_rect(fill = "grey30") )

  

strip.text controls facet header text. Pair it with strip.background = element_rect(...) to restyle the surrounding box.

Warning
hjust and vjust reverse when text is rotated. A vertical y-axis label with angle = 90 reads bottom-to-top, so hjust = 0 puts the start of the word at the bottom of the axis, not the left. Always preview after changing angles.

element_text() vs the other element_*() helpers

Each text slot accepts only element_text() or element_blank(). Other slots accept element_line() or element_rect(). Mixing them throws an error.

Helper Used for Example slot Returns
element_text() Text styling plot.title, axis.text Text spec
element_line() Lines and axes axis.line, panel.grid Line spec
element_rect() Filled rectangles panel.background, strip.background Rect spec
element_blank() Hide an element any slot Empty spec

If you pass element_line() to plot.title, R errors with 'plot.title' must be an element_text object. Match the helper to the slot.

Common pitfalls

Pitfall 1: Forgetting theme(). element_text() returns a specification but renders nothing on its own. Always wrap it: + theme(plot.title = element_text(...)).

Pitfall 2: Wrong slot name. Use axis.title.x (lowercase, dotted). axis_title_x or AxisTitleX will fail silently in older ggplot2 versions and error in newer ones. Type ?theme for the canonical list.

Pitfall 3: Font not found. If family = "Helvetica" is not installed, R falls back to its default sans-serif WITHOUT warning. Register custom fonts first, or stick with "sans", "serif", "mono".

Pitfall 4: Confusing plot.title.position with hjust. hjust aligns within the title box; plot.title.position = "plot" (ggplot2 3.3+) aligns the title to the whole plot area, including the y-axis label space. Use the latter when you want a true left-edge title.

Try it yourself

Try it: Build a scatter plot of mpg vs wt from mtcars. Make the title bold size 16 in navy, rotate the x-axis labels 30 degrees, and italicize the caption. Save to ex_styled.

RYour turn: style every text element
ex_styled <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title = "MPG vs Weight", caption = "Source: mtcars") + # your code here ex_styled #> Expected: a styled scatter plot

  
Click to reveal solution
RSolution
ex_styled <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title = "MPG vs Weight", caption = "Source: mtcars") + theme( plot.title = element_text(size = 16, face = "bold", color = "navy"), axis.text.x = element_text(angle = 30, hjust = 1), plot.caption = element_text(face = "italic") ) ex_styled

  

Explanation: Each text slot gets its own element_text() call. The title and caption styles are isolated; the rotated axis text uses hjust = 1 to align rotated text to the tick marks.

After mastering element_text(), explore these companions:

  • theme(): the wrapper that accepts every element_*() spec
  • element_line(), element_rect(), element_blank(): matching helpers for lines, rectangles, and hidden elements
  • labs(): set the actual title, subtitle, axis names, and caption text
  • margin(): build the margin spec for text padding and plot edges
  • theme_minimal(), theme_classic(), theme_bw(): pre-built base themes to layer styles on
  • showtext package: load Google Fonts and custom system fonts for use in family

For the canonical argument list, see the ggplot2 element_text reference.

FAQ

How do I change the font size of axis labels in ggplot2?

Pass element_text(size = N) to axis.text inside theme(). For example, theme(axis.text = element_text(size = 12)) sets both x and y axis tick labels to size 12. Target a single axis with axis.text.x or axis.text.y. Sizes are in points; 8 to 14 is the readable range for most plots.

Why is my custom font not showing in ggplot2?

R only sees fonts that are registered in its graphics device. Use extrafont::font_import() once to register system fonts, or showtext::font_add_google("Roboto") plus showtext_auto() for Google Fonts. Without registration, R silently falls back to its default sans-serif and your family setting is ignored.

What is the difference between hjust and vjust in element_text?

hjust controls horizontal alignment, 0 is left, 0.5 is center, 1 is right. vjust controls vertical alignment, 0 is bottom, 0.5 is middle, 1 is top. For rotated text, both axes rotate too, so hjust controls alignment along the new horizontal direction of the rotated label. Always preview after changing angle.

Can I use element_text() for plot annotations?

No. element_text() only styles theme slots (titles, axis labels, legends, facet strips). For text drawn inside the plot panel, use geom_text(), geom_label(), or annotate("text", ...), which accept size, fontface, color, and family arguments directly rather than through element_text().

How do I bold only part of a ggplot2 title?

Base ggplot2 does not support partial-bold strings. Use the ggtext package and replace element_text() with element_markdown() from ggtext. Then write your title as "Plot of mpg vs wt" with markdown bold syntax. The package parses the markdown and renders the bold portion correctly.

Prefer to learn by doing? Every track on the learning roadmap opens with a free section of interactive lessons, where you write the R yourself and the page tells you when it is wrong.