R Pipe Operator: %>% vs |> (Complete Guide)
R has two pipe operators: %>% from magrittr (the original) and |> built into base R 4.1+. Both forward the left-hand value into the first argument of the right-hand function, enabling readable left-to-right code chains.
mtcars |> head() # native pipe (base R 4.1+) mtcars %>% head() # magrittr pipe (needs library) mtcars |> filter(mpg > 20) |> nrow() # chained mtcars %>% .$mpg %>% mean() # %>% supports . placeholder mtcars |> (\(x) x$mpg)() |> mean() # |> needs lambda for placeholder df |> head(n = 3) # extra args after pipe mtcars %>% {head(., n = 3)} # %>% with curly-brace expression
Need explanation? Read on for examples and pitfalls.
What R's pipe operators do in one sentence
Both |> and %>% take the value on the left and pass it as the FIRST ARGUMENT to the function on the right. mtcars |> head() is identical to head(mtcars). The pipe form reads left-to-right; the function-call form reads right-to-left.
The |> operator is built into base R from version 4.1 (May 2021). The %>% operator comes from the magrittr package (also re-exported by dplyr and the tidyverse). For 95% of use cases they are interchangeable.
Syntax
The pipe goes between the value and the function, with the function written as if its first argument were already filled.
All three return the same 3-row data frame. The pipe forms are easier to read for chains; the function call form is fine for one or two operations.
|> for new code unless you specifically need %>% features. The native pipe has no package dependency, parses faster, and is the modern default. Reach for %>% only when you need the . placeholder, %T>%, %<>%, or compound { } expressions.Six common patterns
1. Single-step pipe
The simplest case: replace head(mtcars) with mtcars |> head(). Useful at the START of any pipeline so the data flows naturally.
2. Chained pipe
Multiple pipes chain function calls. Each step's output becomes the next step's first argument. Reads top-to-bottom: take mtcars, filter, select, take first 3.
3. Pipe with extra arguments
head() accepts n as a named argument. The pipe puts mtcars as the first positional argument; named args after it work as usual.
4. magrittr . placeholder
%>% supports . to indicate the pipe value's position. Native |> does not (in base R 4.1; partial support added in 4.2 via _ placeholder).
5. Native pipe with a lambda for non-first arg
Without ., native pipe needs a lambda \(d) ... to redirect the input. More verbose than %>%, but no package dependency.
6. Pipe to assignment
Standard pattern: pipe through transformations, finish with mean() or similar, assign the final scalar to a variable.
mtcars |> head() and head(mtcars) are exactly the same operation. The pipe is purely syntactic sugar to read function chains left-to-right. Performance is identical (or microseconds different); choose based on readability.Native pipe (|>) vs magrittr pipe (%>%)
Both forward LHS to the first argument. Differences are at the edges.
| Feature | ` | >` (native) | %>% (magrittr) |
|---|---|---|---|
| Available | R 4.1+ | Any version, needs magrittr | |
| Dependency | None (base R) | magrittr (or dplyr re-export) | |
. placeholder |
No (use _ in 4.2+) |
Yes | |
| Lambda needed for non-first arg | Yes | No (use .) |
|
{ } expression after pipe |
Limited | Yes | |
%T>% (tee) |
No | Yes | |
%<>% (compound assign) |
No | Yes | |
| Parse speed | Faster | Slower (operator overload) | |
| Idiomatic in 2026+ | Increasingly | Legacy |
When to use which:
- Use
|>in new code targeting R 4.1 or later. - Use
%>%when you need the.placeholder,%T>%, or compound assignment. - Mixing both in one file is fine; both work in tidyverse pipelines.
Common pitfalls
Pitfall 1: forgetting parentheses on |>. mtcars |> head (no parens) errors. Native pipe always requires () after the function name. magrittr %>% allows omitting parens if no other args: mtcars %>% head works.
Pitfall 2: piping to a non-first argument with native pipe. 1:5 |> mean(na.rm = TRUE) works because mean() takes the vector as first arg. But 1:5 |> sum(2:3) adds 1:5 + 2:3 (unexpected). To pipe to a specific position, use a lambda: 1:5 |> (\(x) sum(2:3, x))().
_ placeholder in |> (R 4.2+) only works with NAMED arguments. mtcars |> lm(mpg ~ hp, data = _) works. mtcars |> lm(_, mpg ~ hp) errors because _ cannot be a positional argument. Use lambdas if you need positional flexibility.Pitfall 3: confusing |> with >> or other operators in other languages. R's pipe is |> (vertical bar + greater-than). It is NOT ->, >>, or =>. Some languages use |> differently; in R it is strictly "value into first argument".
Try it yourself
Try it: Use the native pipe |> to compute the mean mpg of mtcars cars with hp > 100. Save to ex_mean.
Click to reveal solution
Explanation: Each step's result becomes the next step's first argument. filter(hp > 100) keeps high-hp rows; pull(mpg) extracts mpg as a vector; mean() averages it. Reads top-to-bottom.
Related R operators
After mastering the pipe, look at:
%T>%(magrittr tee operator): pipes value to a function but keeps the original%<>%(compound assignment): updates LHS in place%$%(exposition pipe): exposes column names\(x) ...(R 4.1+ lambda): anonymous function syntax often used with pipes_placeholder (R 4.2+): pipe to a named argument
The pipe pairs naturally with all dplyr verbs and most tidyverse functions because they are designed to take data as the first argument.
FAQ
What is the difference between %>% and |> in R?
%>% is from the magrittr package (also exported by dplyr); |> is built into base R 4.1+. Both forward the LHS value to the first argument of the RHS function. Differences: %>% supports the . placeholder, %T>%, %<>%. |> is faster, has no package dependency, and is the modern default.
Should I use the native pipe |> or magrittr %>%?
For new code, |> is preferred unless you need %>%-specific features like the . placeholder. Both work in tidyverse pipelines. The choice rarely affects performance; pick based on readability and target R version.
How do I pipe to a non-first argument in R?
With %>%, use the . placeholder: df %>% lm(y ~ x, data = .). With |> (R 4.2+), use the _ placeholder for named args only: df |> lm(y ~ x, data = _). For R 4.1 native pipe, use a lambda: df |> (\(d) lm(y ~ x, data = d))().
Does the pipe operator slow down R code?
Negligibly. Native |> adds no measurable overhead. %>% adds microseconds per call due to its operator overload. For any real workload, the pipe's cost is invisible.
Can I use the pipe operator outside the tidyverse?
Yes. Both pipes work with any function that takes data as its first argument. c(3, 1, 2) |> sort() |> head(2) uses base R only. The pipe is independent of the tidyverse, though tidyverse functions are designed to work especially well with it.