tidyr separate_longer_delim() in R: Split Column Into Rows

The separate_longer_delim() function in tidyr 1.3 splits a column's values by a delimiter and creates ONE NEW ROW per part. It is the tidy-data unstacking idiom: comma-separated lists become individual rows.

⚡ Quick Answer
df |> separate_longer_delim(tags, delim = ",")    # one row per tag
df |> separate_longer_delim(items, delim = "; ")
df |> separate_longer_delim(c(col1, col2), delim = ",") # multiple cols
tidyr::separate_rows(df, tags, sep = ",")          # superseded predecessor
df |> separate_wider_delim(...)                    # different: into columns

Need explanation? Read on for examples and pitfalls.

📊 Is separate_longer_delim() the right tool?
STARTcomma-list -> rowsseparate_longer_delim()comma-list -> columnsseparate_wider_delim()old code with separate_rowsworks but supersededpositional split into rowsseparate_longer_position()list-column to rowstidyr::unnest_longer()

What separate_longer_delim() does in one sentence

separate_longer_delim(data, cols, delim) splits each value of cols by delim and creates ONE NEW ROW per resulting part. Other columns' values are duplicated for each new row.

This unstacks delimiter-separated lists into the standard tidy "one observation per row" format.

Syntax

separate_longer_delim(data, cols, delim). delim is a literal string (not regex).

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.
RComma-separated tags to rows
library(tidyr) library(dplyr) df <- tibble(id = 1:2, tags = c("a,b,c","d,e")) df |> separate_longer_delim(tags, delim = ",") #> id tags #> 1 1 a #> 2 1 b #> 3 1 c #> 4 2 d #> 5 2 e

  
Tip
This is a key tidy-data operation: convert "list inside a cell" to "one cell per item, one row per cell". Standard input prep for many downstream operations.

Five common patterns

1. Comma-separated tags

REach tag becomes a row
df <- tibble(post_id = 1:2, tags = c("R,dplyr,tidyr","ggplot2,R")) df |> separate_longer_delim(tags, delim = ",")

  

2. Multi-character delimiter

R'; ' separator
df |> separate_longer_delim(items, delim = "; ")

  

3. Multiple columns at once

RBoth authors and keywords
df |> separate_longer_delim(c(authors, keywords), delim = ",")

  

4. Trim whitespace post-split

RCombine with stringr
library(stringr) df |> separate_longer_delim(tags, delim = ",") |> mutate(tags = str_trim(tags))

  

5. Empty / NA handling

RNAs and empties
df <- tibble(id = 1:3, tags = c("a,b","", NA)) df |> separate_longer_delim(tags, delim = ",") #> id 1 produces 2 rows (a, b) #> id 2 produces 1 row (empty string) #> id 3 produces 1 row (NA)

  
Key Insight
**separate_longer_delim() is the modern replacement for separate_rows().* Same semantics; cleaner name and consistency with separate_wider_. The _longer_ and _wider_ naming aligns with pivot_longer / pivot_wider conventions.

separate_longer_delim() vs separate_rows() vs unnest()

Function Status Input Best for
separate_longer_delim() Recommended String column Modern delimiter-to-rows
tidyr::separate_rows() Superseded String column Same; older API
tidyr::unnest() Recommended List column Already-list-shaped data
unnest_longer() Recommended List column Single column to rows

When to use which:

  • separate_longer_delim for delimiter-separated strings.
  • unnest for list columns (e.g., from JSON).
  • Avoid separate_rows in new code.

A practical workflow

Use separate_longer_delim for "tags" or "tags-like" data normalization.

RInteractive R
posts |> separate_longer_delim(tags, delim = ",") |> mutate(tags = trimws(tags)) |> count(tags, sort = TRUE)

  

Tag frequency analysis from comma-separated tag strings. Common pre-processing for text data.

Common pitfalls

Pitfall 1: forgetting to trim whitespace. "a, b, c" splits to "a", " b", " c". Use mutate(col = trimws(col)) after.

Pitfall 2: confusing wider vs longer. wider splits into COLUMNS. longer splits into ROWS. Pick by what you want.

Warning
separate_longer_delim() requires tidyr 1.3+. Earlier versions only have separate_rows(). Check packageVersion("tidyr") if confused.

Try it yourself

Try it: Split a genres column of comma-separated movie genres into one row per genre. Save to ex_long.

RYour turn: split genres
df <- tibble( movie = c("Inception","La La Land"), genres = c("Sci-Fi,Thriller","Drama,Musical,Romance") ) ex_long <- df |> # your code here nrow(ex_long) #> Expected: 5 (2 + 3)

  
Click to reveal solution
RSolution
ex_long <- df |> separate_longer_delim(genres, delim = ",") ex_long #> movie genres #> 1 Inception Sci-Fi #> 2 Inception Thriller #> 3 La La Land Drama #> 4 La La Land Musical #> 5 La La Land Romance

  

Explanation: Each comma-separated genre becomes its own row; movie name is repeated.

After mastering separate_longer_delim, look at:

  • separate_longer_position(): split into rows by character position
  • separate_wider_delim(): split into columns
  • separate_rows(): superseded predecessor
  • unnest_longer(): list column to rows
  • tidyr::nest() / unnest(): list-column workflows

For combining rows back into delimiter-separated strings, use summarise(col = paste(col, collapse = ",")) after grouping.

FAQ

What does separate_longer_delim do in tidyr?

Splits a column's values by a delimiter and creates one row per resulting part. Other columns are duplicated to match.

What is the difference between separate_longer_delim and separate_rows?

separate_rows is superseded by separate_longer_delim (and separate_longer_position). Same semantics, modern naming.

What is the difference between separate_longer_delim and separate_wider_delim?

longer creates new ROWS (one per part). wider creates new COLUMNS (one per part). Same input; different output shape.

How do I trim whitespace after split?

mutate(col = trimws(col)) after the split. Or strip whitespace with strsplit + trimws before separate.

What happens with NAs?

NA values produce one row with NA. Empty strings produce one row with "". Filter after if you don't want them.