# tibble in R trades silent guessing for loud complaints

> tibble is the tidyverse package that reimplements the R data.frame as the tbl_df class: it refuses to rename columns, coerce types or create row names, and it warns you when a column is missing. The price of that strictness is that it is explicitly not a drop-in replacement for data.frame().

**tidyverse/tibble** — A modern re-imagining of the data frame

- Repository: https://github.com/tidyverse/tibble
- Website: https://tibble.tidyverse.org/
- Stars: 764 · Forks: 140
- Language: R
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/tidyverse-tibble

## The data.frame problem tibble was written to fix

R's built-in data.frame has accumulated decades of behaviour that is convenient in a script and confusing in a package. The project page puts it bluntly, describing tibbles as data frames that are lazy and surly: they do less and they complain more. The list of things tibble refuses to do is short and specific. It does not change variable names or types, and it does not do partial matching. Where base R would quietly accept a mistyped column name and hand back NULL, tibble warns that the column does not exist. Where base R would coerce a character vector into factors or stretch a vector to the length of the frame, tibble leaves your inputs alone. The stated payoff is that problems surface earlier, which the project credits with producing cleaner and more expressive code. The audience is R programmers who write data manipulation code and who have been bitten by silent coercion, not beginners looking for a friendlier spreadsheet.

## The invariants that define the tbl_df class

Four rules do most of the work. First, a tibble never changes the type or the name of an input, so a list column stays a list column and a variable named with a capital letter keeps its capital letter. Second, only values of length 1 are recycled, which means a vector that does not fit the frame is an error rather than a silent truncation. Third, row names are never created. Fourth, `$` never partially matches and warns when a column is absent, and `[` always returns a data frame. That last point surprises people most, because the result of a single bracket subset stays in the tibble class instead of degrading into a plain vector. The project treats these subsetting and assignment rules as a defined set of invariants and keeps a dedicated vignette for them, which signals how much of the real behaviour lives at the edges rather than in the headline description.

## Installing tibble and building your first tibble

The project gives three routes to install. The easiest is to install the whole tidyverse, the alternative is to install the tibble package on its own, and a third path pulls the development version straight from GitHub. Here is the install block as the project prints it, with the development install left commented out so it runs only when you ask for it.

``` r
# The easiest way to get tibble is to install the whole tidyverse:
install.packages("tidyverse")

# Alternatively, install just tibble:
install.packages("tibble")

# Or the the development version from GitHub:
# install.packages("pak")
pak::pak("tidyverse/tibble")
```

For a first real use, load the package and build a frame from column vectors. The value of y is recycled because it has length 1, and z is computed from x and y inside the same call, so the output shows three columns with their types abbreviated underneath the header.

``` r
library(tibble)
tibble(x = 1:5, y = 1, z = x^2 + y)
#> # A tibble: 5 × 3
#>       x     y     z
#>   <int> <dbl> <dbl>
#> 1     1     1     2
#> 2     2     1     5
#> 3     3     1    10
#> 4     4     1    17
#> 5     5     1    26
```

When you would rather type the rows out than derive them, tribble() takes a header row of column names and the rows below it. The output shows the character column beside two numeric ones, which is the type summary you will come to rely on.

``` r
tribble(
  ~x, ~y,  ~z,
  "a", 2,  3.6,
  "b", 1,  8.5
)
#> # A tibble: 2 × 3
#>   x         y     z
#>   <chr> <dbl> <dbl>
#> 1 a         2   3.6
#> 2 b         1   8.5
```

## Printing belongs to pillar, not to tibble

A tibble prints only the rows and columns that fit on one screen, followed by an abbreviation of each column's type, so a frame with a hundred rows does not flood the console and a frame of complex objects stays readable. The project keeps a separate vignette cataloguing how each type is abbreviated. What matters more for anyone extending the package is ownership of the print method: as of tibble 3.1.0 printing is handled entirely by pillar, which also owns the display options and the extension points for them. That split explains why tibble lists formatting among the things it is explicitly not trying to do. It also means the console output you see after installing tibble from CRAN comes from a dependency you do not control, so any custom display behaviour belongs in a pillar extension point rather than in a print method on your own class.

## Why as_tibble() is not a universal converter

The most common disappointment with the package is the assumption that as_tibble() will turn anything into a tibble. The project states the boundary in its non-goals: as_tibble() is for inputs that are already data frames, lists, matrices, or tables. Objects of other shapes are out of scope by design, because tibble does not try to convert arbitrary objects. The second limitation is stated just as plainly: tibble is explicitly not a drop-in replacement for data.frame(). Row names are dropped instead of stored, and arithmetic is not defined across all columns. Both of those follow directly from the invariants rather than being oversights, and both will break code that assumes base R semantics, such as code that writes a key into row names and reads it back later. The migration path the project points to is the invariants vignette, which documents every point where subsetting and assignment differ from data frames.

## Extending tbl_df from another package

The project treats extensibility as a goal rather than a side effect. new_tibble() and tbl_sum() exist so that other packages can build their own classes on top of the tbl_df class, or on the tbl class alone, and a vignette on extending describes how to do it. That matters for the tidyverse ecosystem in particular, because a table subclass that inherits tibble's print behaviour inherits its strictness too, including the rules about partial matching and recycling. The practical implication for an author is that the invariants are part of the contract. If your package promises that a returned object is a tibble, then a user can reasonably expect that subsetting it returns a tibble, that column names come back exactly as given, and that a missing column produces a warning rather than a NULL.

## Related work, releases and the licence question

The project credits data.table and the frame package as inspirations for its print method, and notes that data.table::data.table() shares two of tibble's choices, namely not changing column names and not using row names. The difference lies in what surrounds those rules: tibble drops row names instead of storing them and defines `[` to always return a data frame, and the README makes no such claim about data.table. On the maintenance side, the last push was on 2026-10-10 and the project carries a stable lifecycle badge, with recent releases including v3.3.1 on 2026-01-13 and v3.3.0 on 2025-06-10. The repository ships both a LICENSE and a LICENSE.md file, and GitHub classifies the licence as a custom one it cannot categorise, so anyone embedding the package in a commercial product should read those two files rather than assume an MIT or GPL grant.

## Conclusion

Adopt tibble when you write new analysis code in R and want type and naming mistakes to surface where they happen rather than three transformations later. Do not adopt it as a drop-in swap inside code that depends on row names, on arithmetic across all columns, or on partial matching of column names, because the project states plainly that it is not a drop-in replacement for data.frame(). Before you commit, check your existing pipeline for row.name assumptions, run install.packages("tibble") in a scratch session and confirm that the print output fits your console width, and read vignette("invariants") for the subsetting rules that differ from base R.

## FAQ

### How do I install tibble in R?

The project lists three options: install.packages("tidyverse") for the whole collection, install.packages("tibble") for the single package, or pak::pak("tidyverse/tibble") for the development version from GitHub.

### How is tibble different from data.frame?

A tibble never changes the type or name of its inputs, only recycles values of length 1, never creates row names, never partially matches in `$`, and always returns a data frame from `[`. It is explicitly not a drop-in replacement, because row names are dropped instead of stored and arithmetic is not defined across all columns.

### Can as_tibble() convert any R object into a tibble?

No. as_tibble() is intended for inputs that are already data frames, lists, matrices, or tables, and tibble does not try to convert arbitrary objects.

### Which package handles tibble printing?

As of tibble 3.1.0 printing is handled entirely by pillar, which also owns the display options and the extension points for them.

### How can another package build its own class on tibble?

new_tibble() and tbl_sum() let other packages build classes on the tbl_df class or on the tbl class alone, as the vignette on extending describes.

## Sources

- [Issues](https://github.com/tidyverse/tibble/issues)
- [Project website](https://tibble.tidyverse.org/)
- [README](https://github.com/tidyverse/tibble/blob/main/README.md)
- [Releases](https://github.com/tidyverse/tibble/releases)
- [tidyverse/tibble on GitHub](https://github.com/tidyverse/tibble)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tidyverse-tibble
