tibble::tibble()
tibble(..., .rows = NULL, .name_repair = c("unique", "universal", "minimal", "check_unique", "check_unique", "check_names")) A tibble (pronounced “tibble”) is the tidyverse’s modern take on the classic R data.frame. Created by the tidyverse team, tibbles retain the core structure of data frames but with refinements that make them more predictable and easier to work with in data analysis pipelines.
Unlike traditional data frames, tibbles never automatically convert string vectors to factors, never change column names silently, and always print in a tidy format that shows you the data types and dimensions at a glance.
Syntax
tibble(
...,
.rows = NULL,
.name_repair = c("unique", "universal", "minimal",
"check_unique", "check_unique", "check_names")
)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
... | named arguments | — | Column definitions as name = value pairs |
.rows | integer | NULL | Number of rows (automatically computed from ... if not specified) |
.name_repair | character | "unique" | How to handle duplicate or invalid column names |
Examples
Creating a tibble from scratch
library(tibble)
# Basic tibble creation
df <- tibble(
name = c("Alice", "Bob", "Charlie"),
age = c(25, 30, 35),
score = c(85.5, 92.0, 78.5)
)
# Print displays data types
print(df)
#> # A tibble: 3 × 3
#> name age score
#> <chr> <dbl> <dbl>
#> 1 Alice 25 85.5
#> 2 Bob 30 92.0
#> 3 Charlie 35 78.5
The most visible difference from base R is factor conversion: tibble() never turns strings into factors without asking. Base data.frame() did this silently in R versions before 4.0, and legacy code that relied on the old default can produce factor columns where character was expected. Switching to tibbles eliminates an entire class of silent type-coercion bugs that are painful to trace in production scripts.
Using tibble() vs data.frame()
# tibble: strings stay as character (not converted to factor)
df_tibble <- tibble(name = c("a", "b"), x = 1:2)
str(df_tibble)
#> tibble [2x2], S3: tbl_df/tbl/data.frame
#> $ name: chr [1:2] "a" "b"
#> $ x : int [1:2] 1 2
# data.frame: strings become factors by default
df_base <- data.frame(name = c("a", "b"), x = 1:2)
str(df_base)
#> 'data.frame': 2 obs. of 2 variables:
#> $ name: Factor w/ 2 levels "a","b": 1 2
#> $ x : int 1 2
Recycling behavior is another key divergence. Base data.frame() silently recycles any vector to match the longest column, which can mask data-entry errors where column lengths are accidentally mismatched. Tibbles reject mismatched lengths unless the shorter vector is length 1, in which case it recycles explicitly — a deliberate convenience for scalar constants like tibble(x = 1:3, group = "control").
Column recycling with tibble()
# tibble enforces strict recycling rules
# This would fail (unlike data.frame which recycles silently)
try(tibble(x = 1:3, y = 1:2))
#> Error: tibble() requires each column to be same length
# tibble can recycle length-1 vectors automatically
tibble(x = 1:3, y = 1)
#> # A tibble: 3 × 2
#> x y
#> <int> <dbl>
#> 1 1 1
#> 2 2 1
#> 3 3 1
When you want to enter data in a human-readable, row-by-row format, tribble() (transposed tibble) is the right tool. Column names start with ~, and rows follow one per line. This layout is especially convenient for small lookup tables, test fixtures, and examples in documentation, where reading across rows matches how people naturally organize tabular information on screen or in a script.
Using tribble() for readable data entry
# tribble() allows row-wise data entry
df <- tribble(
~name, ~age, ~city,
"Alice", 25, "NYC",
"Bob", 30, "LA",
"Charlie", 35, "Chicago"
)
print(df)
#> # A tibble: 3 × 3
#> name age city
#> <chr> <dbl> <chr>
#> 1 Alice 25 NYC
#> 2 Bob 30 LA
#> 3 Charlie 35 Chicago
Conversion between data frames and tibbles is a one-way door in practice: as_tibble() is cheap because it only adds the tbl_df class attribute without copying column data. Going back to a base data frame with as.data.frame() also avoids data copy. Use is_tibble() for type checking in functions that need to branch on input type — it returns a simple logical without side effects.
Common patterns
Converting between data.frame and tibble
# Convert data.frame to tibble
as_tibble(iris)
# Convert tibble back to data.frame
as.data.frame(df)
# Check if an object is a tibble
is_tibble(df)
#> [1] TRUE
Subsetting is where the consistency promise pays off most. df[, "x"] on a base data frame returns a vector when there is one column, breaking downstream code that expects a data frame. Adding drop = FALSE everywhere is cumbersome and easy to forget. The tidyverse approach always returns a tbl_df from [, eliminating the need for defensive drop arguments and letting column access behave identically regardless of how many columns you select.
Subsetting behavior
df <- tibble(x = 1:3, y = c("a", "b", "c"))
# Tibble subsetting always returns a tibble
df[, "x"]
#> # A tibble: 3 × 1
#> x
#> <int>
#> 1 1
#> 2 2
#> 3 3
# Using $ returns a vector (not a factor)
df$x
#> [1] "a" "b" "c"
Name repair handles the real-world problem of messy column names — spaces, special characters, leading digits, duplicates — that arrive from imported spreadsheets and database exports. Rather than silently accepting invalid names or auto-converting them unpredictably, .name_repair lets you choose the strategy: "unique" appends a suffix to duplicates, "universal" makes all names syntactic R identifiers, and "minimal" leaves them as-is at your own risk.
Name repair
# Handle duplicate column names
df <- tibble(
"x" = 1,
"x" = 2,
.name_repair = "unique"
)
names(df)
#> [1] "x" "x.1"
# Universal names (make syntactic)
df <- tibble(
"my var" = 1,
"123" = 2,
.name_repair = "universal"
)
names(df)
#> [1] "my.var" "X123"
Tibble vs data.frame behavior
These structures are strict about type coercion: they never silently convert strings to factors and they do not recycle vectors of length greater than 1. These restrictions catch a class of bugs that silent recycling and factor conversion cause in base R data frames.
The printing behavior differs from data frames. They display the first 10 rows and all columns that fit the terminal width, with a type annotation below each column name (<chr>, <dbl>, <int>, etc.). This compact summary is often more useful than head() for quickly understanding a dataset’s structure.
The subsetting behavior also differs: df[, 1] returns a data frame when df is a tbl_df (preserving the class), but returns a vector when df is a plain data frame. This consistency means you do not need to add drop = FALSE to preserve the data-frame class when subsetting to one column.
All tidyverse functions return tbl_df objects. If you pass a data frame in, most functions return a tbl_df back, which means you can mix both types in a pipeline but should expect that output type.
When passing a tbl_df to functions written for base R data.frame, you can usually pass it directly since it inherits from data.frame. The exception is code that relies on data.frame’s partial column name matching (df$nam matching df$name) or single-column subsetting that drops the data-frame class — these behaviors differ in the newer type. Use as.data.frame(tbl) to get a plain data frame when full base R compatibility is required. The conversion is cheap and does not copy the underlying column data.