tidyr unnest_wider() in R: Spread Named Lists Into Columns
The unnest_wider() function in tidyr spreads a list-column where each cell is a NAMED LIST into multiple new columns, one per name. It is essential for unnesting JSON-shaped data.
df |> unnest_wider(json_col) # spread named list to columns df |> unnest_wider(json_col, names_sep = "_") # prefix new column names df |> unnest_wider(json_col, names_repair = "unique") df |> unnest_longer(col) # different: vectors to rows df |> unnest(col) # different: tibbles to rows
Need explanation? Read on for examples and pitfalls.
What unnest_wider() does in one sentence
unnest_wider(data, col, names_sep = NULL) spreads a list-column where each cell is a named list (or 1-row tibble) into multiple new columns, named after the list elements. Each name in the list becomes a new column.
Syntax
unnest_wider(data, col, names_sep = NULL, names_repair = "check_unique"). names_sep prefixes column names.
unnest_wider after jsonlite::fromJSON() to extract JSON object fields into separate columns. Common ETL pattern for API responses.Five common patterns
1. Standard wide unnest
2. Prefix new column names
3. JSON parsing workflow
4. From a list of one-row tibbles
5. Handle missing names
unnest_wider is the standard tool for "JSON object -> columns". Combined with purrr::map() and jsonlite::fromJSON, it turns nested API responses into tabular data.unnest_wider() vs unnest_longer() vs unnest() vs hoist
| Function | Input shape | Output |
|---|---|---|
unnest_wider() |
Named lists | New columns |
unnest_longer() |
Vectors | New rows |
unnest() |
Tibbles | New rows |
hoist() |
Named lists, specific elements | Specific new columns |
When to use which:
- unnest_wider for ALL fields of a named list.
- hoist for SPECIFIC fields by name.
- unnest_longer for vectors.
- unnest for tibbles.
A practical workflow
The "API response to data frame" pattern uses unnest_wider extensively.
JSON with nested objects requires multiple unnest_wider calls.
Common pitfalls
Pitfall 1: name conflicts. If the list contains a name that already exists in the data frame (e.g., both have an "id"), unnest_wider errors. Use names_sep or names_repair to handle.
Pitfall 2: confusing with unnest. unnest expects each cell to be a tibble; output is rows. unnest_wider also works on 1-row tibbles but more naturally on named lists; output is columns.
unnest_wider() may produce many NA values if the list elements have different names. Each row gets a column for EVERY name seen across all list cells.Try it yourself
Try it: Spread a list column of list(score, grade) into two columns. Save to ex_wide.
Click to reveal solution
Explanation: Each list's named elements become columns. score and grade are extracted from each cell.
Related tidyr / purrr functions
After mastering unnest_wider, look at:
unnest_longer(): vectors to rowsunnest(): tibbles to rowshoist(): extract specific elementspack(): opposite (combine columns into list)jsonlite::fromJSON(): JSON to R objects
FAQ
What does unnest_wider do in tidyr?
unnest_wider(data, col) spreads a list column where each cell is a named list (or 1-row tibble) into multiple new columns, named after the list elements.
What is the difference between unnest_wider and unnest_longer?
unnest_wider creates new COLUMNS (one per name). unnest_longer creates new ROWS (one per element). Different output shapes.
How do I avoid column name conflicts with unnest_wider?
Pass names_sep = "_" to prefix new column names with the original list-column's name, or use names_repair = "unique".
Can I use unnest_wider on JSON?
Yes. After parsing JSON with jsonlite::fromJSON(), unnest_wider spreads each top-level field into a column.
What if my list elements have different names?
unnest_wider creates a column for EVERY name seen across all cells. Cells without that name get NA in the corresponding column.