-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server Side Rendering #22
Comments
Yes, I'd like to implement server-side rendering eventually, but it'll probably be a while before I get there. For now, I'd definitely recommend using DT for large tables in Shiny apps. For this example, it looks like most of the time comes from rendering the row details. So as a workaround, I think it'll be faster to use a JavaScript render function if possible, or avoid Shiny/htmltools tags. HTML tag rendering seems to be pretty slow at the moment (which I also noticed in #17). |
The server-side rendering would be indeed a fantastic addition to |
This is a good example with |
Did you make any progress with this @timelyportfolio ? Server-side rendering is sorely missed in reactable, and really limits scalability. |
Hey @glin , have you seen this awesome react-base-table project ? It looks promising. |
One vote for the server side rendering feature. That's why some tables still use DT. |
Hello, plus topic. Now a lot of pain comes from rewriting the project on a WEB datatable, which is VERY raw and unpleasant in web layout (problems are just at every step), unlike reactable. But server-side optimization solved it. I really want to see server-side in reactable... P.S.: By the way, the datatable has well-implemented modifications to the painting of each cell - https://rstudio.github.io/DT/functions.html. They work pretty fast, there is enough functionality in general. I would like something similar for reactable, because looping through each cell in R is quite slow. |
Quick update: I'm working on this now, thanks to support from Posit giving me some dedicated time to work on this. There's currently experimental support for server-side data in the latest development version. If you want to try it out, you can install the latest dev version from GitHub, then install the devtools::install_github("glin/reactable")
install.packages("V8") And then set library(shiny)
library(reactable)
tbl <- reactable(
mtcars,
server = TRUE
)
ui <- fluidPage(
reactableOutput("tbl")
)
server <- function(input, output) {
output$tbl <- renderReactable({
tbl
})
}
shinyApp(ui, server) Most features are implemented, but there's still some remaining work to do, including:
Going into more detail: the default server-side backend is running JavaScript under the hood (via Running JavaScript on the server not only allows for major code reuse (i.e. easier maintenance, features always work the same in client-side and server-side mode), but also big performance gains compared to an R implementation based on data.frames. Data manipulation in JavaScript/V8 is just so fast by default, especially for sorting and filtering. And the JavaScript library already has a bunch of memoization/caching built-in via React, so we get all of that for free. (For example, paginating a sorted/filtered table won't recalculate the sorting/filtering). There are still some drawbacks with this approach though, like very large datasets may run up against V8's 4 GB heap limit and crash, or take a noticeable few seconds to initialize. But I think it'll work well for most use cases, with custom swappable backends covering everything else. If you want to test the performance, here's the 100k rows example converted to a Shiny app. Running locally on my system, most operations take <200 ms at most (sorting <200ms, searching <50ms, pagination <10ms). You can change library(shiny)
library(reactable)
rows <- 100000
dates <- seq.Date(as.Date("2018-01-01"), as.Date("2018-12-01"), "day")
data <- data.frame(
index = seq_len(rows),
date = sample(dates, rows, replace = TRUE),
city = sample(names(precip), rows, replace = TRUE),
state = sample(rownames(USArrests), rows, replace = TRUE),
temp = round(runif(rows, 0, 100), 1),
stringsAsFactors = FALSE
)
tbl <- reactable(
data,
filterable = TRUE,
searchable = TRUE,
minRows = 10,
highlight = TRUE,
columns = list(
state = colDef(
html = TRUE,
cell = JS("function(cell) {
return '<a href=\"https://wikipedia.org/wiki/' + cell.value + '\">' + cell.value + '</a>'
}")
)
),
details = colDef(
html = TRUE,
details = JS("function(rowInfo) {
return 'Details for row: ' + rowInfo.index +
'<pre>' + JSON.stringify(rowInfo.row, null, 2) + '</pre>'
}")
),
showPageSizeOptions = TRUE,
server = TRUE
)
ui <- fluidPage(
reactableOutput("tbl")
)
server <- function(input, output) {
output$tbl <- renderReactable({
tbl
})
}
shinyApp(ui, server) |
This is fantastic news, we appreciate a lot. Will it allow infinite vertical scroll instead of pagination (which is the other feature I most miss)? |
Hey Greg - thanks so much for creating/maintaining the reactable package, I use it so much. I came across this post as similar to OP Sbirch556, I have a reactable in an R Shiny dashboard with an R function for custom rendering expandable row details. I actually copied the R function from the CRAN Packages demo and have added a dynamically generated button for each record to export to PNG using the Initially, I was rendering a data source that only ever had a couple hundred records, max. However, I have since added a larger data source that can range from a few hundred to c.50k records, depending on the user's input (which forms an SQL query to fetch data for the reactable). I currently have logic prior to creating the reactable with fetched data to determine Your latest dev seems promising that perhaps I can use the R render function on server-side. As a test, I downloaded the dev version of reactable and the V8 package. devtools::install_github("glin/reactable")
install.packages("V8") I then replaced the JavaScript render function in the example you gave above with the OP's R render function. Note: I also reduced the number of row to ten thousand. library(shiny)
library(reactable)
#rows <- 100000
rows <- 10000
dates <- seq.Date(as.Date("2018-01-01"), as.Date("2018-12-01"), "day")
data <- data.frame(
index = seq_len(rows),
date = sample(dates, rows, replace = TRUE),
city = sample(names(precip), rows, replace = TRUE),
state = sample(rownames(USArrests), rows, replace = TRUE),
temp = round(runif(rows, 0, 100), 1),
stringsAsFactors = FALSE
)
then <- Sys.time()
tbl <- reactable(
data,
filterable = TRUE,
searchable = TRUE,
minRows = 10,
highlight = TRUE,
columns = list(
state = colDef(
html = TRUE,
cell = JS("function(cell) {
return '<a href=\"https://wikipedia.org/wiki/' + cell.value + '\">' + cell.value + '</a>'
}")
)
),
details = function(index){
# Print index to console
print(paste0("index: ", index))
tabsetPanel(
tabPanel("Data", data[index,])
)
},
showPageSizeOptions = TRUE,
server = TRUE
)
now <- Sys.time()
elapsed <- now - then
print(paste0("elapsed:", elapsed)) #"elapsed:39.0062990188599"
ui <- fluidPage(
reactableOutput("tbl")
)
server <- function(input, output) {
output$tbl <- renderReactable({
tbl
})
}
shinyApp(ui, server) The time to load the shiny app and render the reactable is still really long. I note that it takes about 40 seconds to churn through the R function itself and create the reactable. It then takes another 30-odd seconds to render the table on the shiny app. This is all run locally on my machine. My two questions are:
Thanks again for a brilliant package! |
@GitHunter0 It won't, and virtualized scrolling is a completely separate feature from server-side data support. But if virtualized scrolling is implemented in the feature, then it would theoretically "just work "with server-side data. You can follow this existing issue for table virtualization: #203
|
@BilboBaagins The current implementation does nothing to help that use case yet, and all R render functions are still run for the entire table up front. However, it is on the list of remaining things to do, and I'm still thinking about how it would work. There are some cases where you might want to precalculate all custom rendered content up front (because doing it on demand would be slow), and there are some cases where you would want to do it per page, and I'm not really sure how to accommodate both in a nice way. For huge tables, I would recommend going JavaScript render functions all the way if you can though, as those would be both lazy-run and efficient if all it does it generate HTML from table data. And unfortunately no, I haven't seen any JavaScript versions of the CRAN Packages demo, and I don't think it would be very easy because of the embedded R htmlwidget in there. The Popular Movies example does have some examples of generating complicated HTML from JavaScript that may be useful though. |
I am seeing that when I set server=TRUE, it is taking 3 seconds longer to render my table. I get this warning when I load my app package:
Would that prevent server-side rendering? |
@glin Thank you for the package. My data has 4 K rows and will increase over time. My reactable is slowing down using shiny::icon, tags ( a, div, and span ) on multiple columns that add color, or an icon , or hyperlink, and bar chart. When I try the examples, from the suggested custom javascript to speed up link, the JS example code render a blank page, but the R render works. Do I need to install any packages to JS to work ?
|
+1 for server side! Though I'm going to give the dev a try. |
I've been testing server-side rendering to see if I can implement my own S3 object based on some changes to https://github.com/glin/reactable/blob/main/R/server-df.R.
edit, when inspecting a bit the v8 backend and the structure it generates, and comparing what the df backend has, it looks to me that the df backend needs the following in order to make it work for one level of groupby to be put here: https://github.com/glin/reactable/blob/main/R/server-df.R#L142
|
Not sure if I'm hijacking this thread but as this is all related to server side-rendering which is currently experimental.
|
There are still some areas that need to be improved in this server virtualization method. For example, when switching pages, the selection function will be reset and all previously selected options will be refreshed |
Possible to implement some type of server side rendering similar to data.table R package does? Below example takes some time to render.
The text was updated successfully, but these errors were encountered: