Learn R Programming

{editbl}: DT extension for CRUD

editbl ('edit tibble') allows you to modify tables in a spreadsheet-like fashion. Not just in-memory data.frame objects, but also data living in a database.

Installation

  • From CRAN:
install.packages('editbl')
  • Latest development version:
remotes::install_github("https://github.com/openanalytics/editbl", ref = "main", subdir = "editbl")

Get started

Choose a dataset of your liking and use eDT to interactively explore and modify it!

modifiedData <- editbl::eDT(mtcars)
print(modifiedData)

Run some demo apps

editbl::runDemoApp()

More introductory examples can be found here. Advanced examples can be found in the vignettes.

Features

Overview of main features

  • Supporting multiple backends and in-place editing
  • Customizable (lightweight DT wrapper)
  • Easy integration in shiny apps
  • Undo/redo button
  • Copy rows
  • Drag cells
  • No need to have all data in-memory
  • Tackles challenges such as enforcing foreign keys and hiding of surrogate keys
  • Transactional commits (currently for tbl_dbi class and non in-place editing)
  • Default values for new rows (UUID's, 'current date', 'inserted by', ...)
  • Possible to set row level security

Constraints and normalized tables

Sometimes you want to restrict certain columns of your table to only contain specific values. Many of these restrictions would be implemented at database level through the use of foreign keys to other tables.

editbl allows you to specify similar rules through the use of foreignTbls as an argument to eDT(). Note that you can additionally hide surrogate keys by the use of naturalKey and columnDefs if you wish to.

a <- tibble::tibble(
    first_name = c("Albert","Donald","Mickey"),
    last_name_id = c(1,2,2)
  )

b <-  foreignTbl(
  a,
  tibble::tibble(
      last_name = c("Einstein", "Duck", "Mouse"),
      last_name_id = c(1,2,3)
    ),
  by = "last_name_id",
  naturalKey = "last_name"
)

eDT(a,
  foreignTbls = list(b),
  options = list(columnDefs = list(list(visible=FALSE, targets="last_name_id")))
)

Support for different backends

dplyr code is used for all needed data manipulations and it is recommended to pass on your data as a tbl. This allows editbl to support multiple backends through the usage of other packages like dtplyr, dbplyr etc.

In case you pass on other tabular objects like data.frame or data.table the function will internally automatically cast back and forth to tbl. Small side effects may occur because of this (like loosing rownames), so it might be better to cast yourself to tbl explicitly first.

# tibble support
modifiedData <- editbl::eDT(tibble::as_tibble(mtcars))

# data.frame support
modifiedData <- editbl::eDT(mtcars)

# data.table support
modifiedData <- editbl::eDT(data.table::data.table(mtcars))

# database support
tmpFile <- tempfile(fileext = ".sqlite")
file.copy(system.file("extdata", "chinook.sqlite", package = 'editbl'), tmpFile)
conn <- editbl::connectDB(dbname = tmpFile)
modifiedData <- editbl::eDT(dplyr::tbl(conn, "Artist"), in_place = TRUE)
DBI::dbDisconnect(conn)
unlink(tmpFile)

Note that there are some custom methods in the package itself for rows_update / rows_delete / rows_insert. The goal would be to fully rely on dplyr once these functions are not experimental anymore and support all needed requirements. These functions also explain the high amount of 'suggested' packages, while the core functionality of editbl has few dependencies.

Switching from DT

Let's say you already use DT::datatable() to display your data, but want to switch to editbl::eDT() to be able to edit it. Would this be a lot of effort? No! In fact, eDT() accepts the exact same arguments. So it is almost as easy as replacing the functions and you are done. Should you run into problems take a look here for some pointers to look out for.

Notes

  • https://github.com/tidyverse/dtplyr/issues/260 might cause errors / warnings when using eDT with dtplyr. If possible convert to normal tibble first.
  • editbl assumes that all rows in your table are unique. This assumption is the key (ba dum tss) to allow for only having the data partially in memory.
  • editbl does not attempt to detect/give notifications on concurrent updates by other users to the same data, nor does it 'lock' the rows you are updating.

It just sends its updates to the backend by matching on the keys of a row. If other users have in the meantime made conflicting adjustments, the changes you made might not be executed correctly or errors might be thrown.

General future goals for this package

  • Full dplyr compatibility so support for different backends is easily facilitated. Now there are 2 methods (e_rows_update, e_rows_insert) that need to be implemented to support a new backend.
  • Full DT compatibility, including all extensions.
  • Better editing / display options for time values. E.g. control over timezone and format of display / storage + nicer input forms.
  • Any addition that supports the concept of editing data as flexible/easy as possible while respecting backend schema's and constraints.

References

Alternatives

These are other popular CRUD packages in R. Depending on your needs, they might be better alternatives.

DataEditR

  • Rstudio plugin
  • Really flexible excel-like feeling
  • Can only edit in-memory tables. Harder to support databases etc.

editData

  • Rstudio plugin
  • Nice features in terms of editing (pop-ups, more buttons,...)
  • Can only edit in-memory tables. Harder to support databases etc.

Editor

  • Premium datatable extension allowing for editing data.

DT-Editor

  • data.table focused

DTedit

  • DT extension
  • Very customizable (own callbacks)
  • Few dependencies

Additional links:

CRAN DT

CRAN tibble

Blogpost spreadsheets vs robust backends

Blogpost buttons in DT

Blogpost shiny vs excel

Generic CRUD application

Example SQLite database

Copy Link

Version

Install

install.packages('editbl')

Monthly Downloads

564

Version

1.3.0

License

GPL-3

Issues

Pull Requests

Stars

Forks

Maintainer

Jasper Schelfhout

Last Published

April 24th, 2025

Functions in editbl (1.3.0)

coerceValue

DT::coerceValue with better POSIXct support
demoServer_DB

Server of the DB demo app
demoServer_mtcars

Server of the mtcars demo app
eDT_app_ui

UI of eDT_app
e_rows_insert.dtplyr_step

rows_insert implementation for data.table backends.
e_rows_insert.default

Insert rows into a tibble
e_rows_insert

Insert rows into a tibble
demoServer_custom

Server of the mtcars demo app
commitTransaction

Start a transaction for a tibble
demoUI_DB

UI of the DB demo app
demoUI_custom

UI of the demo mtcars app
createEditButtonHTML

Generate HTML for an in-row edit button
eDT_app

Open interactive app to explore and modify data
eDT_app_server

Server of eDT_app
createEditButtonHTML_shiny

Helper function to write HTML
eDT

Create a modifieable datatable.
eDTOutput

UI part of eDT
devServer

Server of the development app
devUI

UI of the development app
e_rows_update.dtplyr_step

rows_update implementation for data.table backends.
disableDoubleClickButtonCss

Function to generate CSS to disable clicking events on a column
demoUI_mtcars

UI of the demo mtcars app
inputUI

An input UI for a data.frame
inputUI.default

UI part for modal with input fields for editing
e_rows_insert.tbl_dbi

rows_insert implementation for DBI backends.
e_rows_update.default

Update rows of a tibble
e_rows_update

Update rows of a tibble
e_rows_update.data.frame

rows_update implementation for data.frame backends.
get_db_table_name

Get name of the tbl in the database
evalCanCloneRow

Determine if a row can be cloned
getColumnTypeSums

Get types of columns in a tbl
getNonNaturalKeyCols

Get all columns that are not natural keys
joinForeignTbl

Merge a tbl with it a foreignTbl
evalCanDeleteRow

Determine if a row can be deleted
e_rows_update.tbl_dbi

rows_update implementation for DBI backends.
fixInteger64

Replace instances of integer64 with actual NA values instead of weird default 9218868437227407266
inputServer.default

An input server for a data.frame
runDemoApp_custom

Run a custom demo app
runDemoApp_mtcars

Run a demo app
inputServer

An input server for a data.frame
rollbackTransaction

Start a transaction for a tibble
standardizeArgument_colnames

Standardize colnames argument to the format of named character vector
overwriteDefaults

Overwrite default settings with provided settings
standardizeArgument_editable

Standardized editable argument to be in the form of a list
foreignTbl

Create a foreign tibble
runDevApp

Run a development app
rows_delete.dtplyr_step

rows_delete implementation for data.table backends.
initData

Add some extra columns to data to allow for / keep track of modifications
rowUpdate

Update rows in the database.
selectInputDT_UI

UI part of a DT select input
rowInsert

Add a row to a table in the database.
selectInputDT_Server

Server part to use a datatable as select input
shinyInput

Get a shiny input for a column of a tbl
evalCanEditRow

Determine if a row can be edited
fillDeductedColumns

Fill data columns based on foreignTbls
runDemoApp

Run a demo app
whereSQL

Generate where sql
runDemoApp_DB

Run a demo app
canXXXRowTemplate

Re-usable documentation
castFromTbl

Cast tbl to class of template
checkForeignTbls

Check if all rows in tbl fufill foreignTbl constraints
castToTemplate

Cast tbl or data.frame x to the types of the template
beginTransaction

Start a transaction for a tibble
castToSQLSupportedType

Cast the data type to something supported by SQL.
addButtons

Add modification buttons as a column
castForDisplay

Cast columns in data.frame to editable types in datatable
coalesce

Return first non NULL argument
coerceColumns

Cast columns to the type of the template
castToFactor

Cast all columns that exist in a foreignTbl to factor
castToTbl

Cast data to tbl
createCloneButtonHTML

Generate HTML for an in-row clone button
customButton

Generate a custom button for eDT
createCloneButtonHTML_shiny

Helper function to write HTML
createDeleteButtonHTML_shiny

Helper function to write HTML
createButtons

Create buttons to modify the row.
connectDB

Connect to a database.
createDeleteButtonHTML

Generate HTML for an in-row delete button