ggplot2 element_line() in R: Style Lines and Gridlines

The element_line() function in ggplot2 styles every line on a plot, axis lines, gridlines, tick marks, and panel borders. You pass it inside theme() with arguments like color, linewidth, linetype, lineend, and arrow.

⚡ Quick Answer
element_line(color = "grey80")                         # subtle gridlines
element_line(linewidth = 0.8)                          # thicker line
element_line(linetype = "dashed")                      # dashed style
element_line(color = "black", linewidth = 0.5)         # crisp axis line
element_line(color = NA)                               # invisible but reserves space
element_line(arrow = arrow(length = unit(2, "mm")))    # axis arrows
element_line(color = "grey90", linetype = "dotted")    # dotted minor grid

Need explanation? Read on for examples and pitfalls.

📊 Is element_line() the right tool?
STARTstyle a line element inside theme()element_line(color = "grey80")style a text label or titleelement_text(size = 14)style a filled panel or strip backgroundelement_rect(fill = "white")remove a line element completelyelement_blank()change axis tick spacing or breaksscale_x_continuous(breaks = ...)pick a built-in theme that resets all linestheme_minimal(), theme_classic()

What element_line() does in ggplot2

element_line() builds a line-styling specification that ggplot2 plugs into any line slot. It does not draw a line by itself. You always pass the result to theme() as the value of a line argument, like panel.grid.major = element_line(color = "grey80").

Every line element in a ggplot2 plot has a name: axis.line, axis.ticks, panel.grid.major, panel.grid.minor. Each one accepts an element_line() value. Specific slots like axis.line.x and panel.grid.major.y let you target one direction at a time. Settings cascade, line = element_line(color = "blue") propagates to every line slot, then specific slots override the cascade.

Key Insight
element_line() is a specification, not a drawn line. ggplot2 stores the spec, then applies it when rendering. This is why one base theme can restyle every gridline across 50 plots at once.

Syntax and arguments

element_line(color, linewidth, linetype, lineend, arrow, inherit.blank) covers every line 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) library(grid) p <- ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) + geom_point(size = 3) + labs(title = "Fuel Economy by Weight", x = "Weight (1000 lbs)", y = "Miles per gallon", color = "Cylinders") p

  

The default theme_grey() draws white major gridlines on a grey panel, no axis lines, and short black tick marks. Every line you see can be restyled with one element_line() per slot.

Argument Type Common values
color (or colour) character "black", "grey80", "#1f77b4", NA
linewidth numeric 0.25 to 2 (mm); 0.5 is the default
linetype character or integer "solid", "dashed", "dotted", "dotdash", "longdash"
lineend character "butt" (default), "round", "square"
arrow grid::arrow() arrow(length = unit(2, "mm"))
inherit.blank logical TRUE inherits element_blank() from parent slot
Note
linewidth replaced size in ggplot2 3.4.0. Older tutorials use element_line(size = 1). New code should use linewidth; size still works for backward compatibility but throws a deprecation warning.

Seven styling patterns with element_line()

1. Subtle major gridlines

RSoft grey major gridlines on a clean panel
p + theme( panel.background = element_rect(fill = "white"), panel.grid.major = element_line(color = "grey85"), panel.grid.minor = element_blank() )

  

A white background with grey85 major gridlines is the cleanest baseline. Setting panel.grid.minor = element_blank() removes the fine minor grid entirely; readers focus on the major breaks.

2. Dashed gridlines

RDashed major gridlines for a technical feel
p + theme( panel.grid.major = element_line(color = "grey70", linetype = "dashed"), panel.grid.minor = element_line(color = "grey90", linetype = "dotted") )

  

linetype accepts six string presets or integers 0 through 6. Mixing dashed major and dotted minor gridlines visually separates the two without color clash.

3. Add visible axis lines

RBlack axis lines, no gridlines
p + theme( axis.line = element_line(color = "black", linewidth = 0.5), panel.grid = element_blank(), panel.background = element_rect(fill = "white") )

  

axis.line is hidden by default in theme_grey(). Adding element_line(color = "black") mimics a base R plot. panel.grid = element_blank() is shorthand that hides both major and minor gridlines at once.

4. Thicker, colored axis lines

RBold navy axes for high contrast
p + theme( axis.line = element_line(color = "navy", linewidth = 1), axis.ticks = element_line(color = "navy", linewidth = 0.8), panel.grid = element_blank() )

  

linewidth is in millimeters. Values above 1 read as bold; values below 0.3 disappear on most screens. Match axis.ticks to axis.line for visual unity.

Tip
Style axis lines and ticks together. Mismatched widths look unintentional. A common pairing: axis.line = element_line(linewidth = 0.5) plus axis.ticks = element_line(linewidth = 0.4).

5. Different styles for x and y

RHorizontal-only gridlines for a Cleveland dot-plot look
p + theme( panel.grid.major.x = element_blank(), panel.grid.major.y = element_line(color = "grey80"), panel.grid.minor = element_blank(), axis.line.x = element_line(color = "black") )

  

panel.grid.major.x controls vertical gridlines; panel.grid.major.y controls horizontal ones. Pairing them with element_blank() and element_line() makes one axis primary.

6. Arrows on axes

RArrowheads at axis ends, classic textbook style
p + theme( axis.line = element_line( color = "black", linewidth = 0.5, arrow = arrow(length = unit(2, "mm"), type = "closed") ), panel.grid = element_blank(), panel.background = element_rect(fill = "white") )

  

arrow() comes from the grid package. length sets the arrowhead size; type = "closed" fills the arrowhead. Use this for textbook diagrams or schematic plots.

7. Invisible but space-preserving

RReserve gridline space without drawing it
p + theme( panel.grid.major = element_line(color = NA), panel.grid.minor = element_blank() )

  

color = NA draws nothing but keeps the layout slot allocated. This differs from element_blank(), which fully drops the element. The difference matters when aligning multiple plots with patchwork; an empty element_blank() can shift panel sizes.

Warning
element_line(color = NA) and element_blank() are not interchangeable. Both look empty, but NA reserves space; element_blank() removes it. Pick element_blank() when you want every plot pixel back; pick NA when alignment with sibling plots matters.

element_line() vs the other element_*() helpers

Each line slot accepts only element_line() or element_blank(). Passing element_text() or element_rect() to a line slot throws an error.

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

If you assign element_text() to panel.grid.major, R errors with 'panel.grid.major' must be an element_line object. Match the helper to the slot.

Common pitfalls

Pitfall 1: Using size instead of linewidth. In ggplot2 3.4 and later, element_line(size = 1) triggers a deprecation warning. Use linewidth = 1 for line thickness; size now applies only to point and text elements.

Pitfall 2: Confusing element_blank() with element_line(color = NA). Both hide the line, but element_blank() removes the layout slot while element_line(color = NA) preserves it. Plot alignment in patchwork breaks if you mix them.

Pitfall 3: Wrong slot name. Use panel.grid.major.x, not panel.grid.major_x or panel.grid.x.major. Type ?theme for the canonical slot list; misnamed slots fail silently in older ggplot2 and error in newer versions.

Pitfall 4: Forgetting theme(). element_line(color = "red") on its own does nothing. Always wrap it: + theme(panel.grid.major = element_line(color = "red")).

Try it yourself

Try it: Build a scatter of mpg vs wt from mtcars. Make the major gridlines dashed grey, hide the minor gridlines, add black axis lines, and use a white panel background. Save the result to ex_lined.

RYour turn: style every line
ex_lined <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + # your code here ex_lined #> Expected: a clean plot with dashed grey major gridlines and black axes

  
Click to reveal solution
RSolution
ex_lined <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme( panel.background = element_rect(fill = "white"), panel.grid.major = element_line(color = "grey80", linetype = "dashed"), panel.grid.minor = element_blank(), axis.line = element_line(color = "black", linewidth = 0.5) ) ex_lined

  

Explanation: Each line slot gets its own element_line() call. The white panel.background lets the grey gridlines show; element_blank() removes the minor grid entirely; the black axis.line adds visible axes that theme_grey() hides by default.

After mastering element_line(), explore these companions:

  • theme(): the wrapper that accepts every element_*() spec
  • element_text(), element_rect(), element_blank(): matching helpers for text, rectangles, and hidden elements
  • arrow() from the grid package: build arrowhead specs for axis lines
  • theme_minimal(), theme_classic(), theme_bw(): pre-built themes with distinct line styling defaults
  • coord_cartesian(): pair with axis lines to control where they meet the panel edge
  • scale_x_continuous(): set tick break positions that gridlines follow

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

FAQ

How do I remove gridlines in ggplot2?

Set panel.grid = element_blank() inside theme() to remove both major and minor gridlines. To remove only one set, target the specific slot: panel.grid.minor = element_blank() keeps the major grid while dropping the minor. To remove gridlines on one axis only, use panel.grid.major.x = element_blank() or panel.grid.major.y = element_blank(). The element_blank() helper drops both the line and the layout space.

What is the difference between linewidth and size in element_line?

linewidth is the modern argument for line thickness in ggplot2 3.4 and later; size was deprecated for line elements because it overloaded with point and text sizing. Both still work, but size issues a deprecation warning. New code should use linewidth = 0.5 (the default) and reserve size for geom_point() and geom_text() calls.

Can I use element_line() to draw lines inside the plot?

No. element_line() only styles theme slots, axis lines, ticks, gridlines, and panel borders. To draw lines inside the plot panel, use geom_hline(), geom_vline(), geom_abline(), or geom_segment(). These geoms accept their own color, linewidth, and linetype arguments directly, without element_line().

How do I make gridlines dashed in ggplot2?

Pass linetype = "dashed" to element_line() for the grid slot you want to restyle. For example, panel.grid.major = element_line(linetype = "dashed", color = "grey70") makes the major gridlines dashed grey. The full set of presets is "solid", "dashed", "dotted", "dotdash", "longdash", and "twodash". Integer codes 0 through 6 also work.

Why are my axis lines not showing in ggplot2?

The default theme_grey() hides axis.line because it relies on the panel border to delimit the plot. Add theme(axis.line = element_line(color = "black")) to make them visible. If you want axes but no panel border, also set panel.border = element_blank(). Built-in themes like theme_classic() enable axis lines automatically.