You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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).
Reacted by Sean Birchall, sphereC, Alfredo Hernández, Mihir Patel, sam, Vitali Khvatkov, Thomas and W48B1T
Hey @glin , have you seen this awesome react-base-table project ? It looks promising.
There is also glide-data-grid, the project which Streamlit based on its new dataframe display.
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 V8 package (required for server-side data but not a hard dependency):
Most features are implemented, but there's still some remaining work to do, including:
Server-side row selection and row expansion aren't implemented yet, and currently only work in client-side mode
Custom server-side backends aren't supported yet. It should eventually be possible to swap in your own custom backend that pulls data from an external database or API (i.e., data not in memory), or one that uses data.table/dplyr.
And shout-out to Ryszard Szymański who pretty much already did this with the impressive Fast Big Data Tables in Shiny table contest submission more than a year ago!
There's no documentation yet
Going into more detail: the default server-side backend is running JavaScript under the hood (via V8), pretty much the exact same code that runs in your browser for client-side tables. It's similar to the recent Static Rendering feature, except the table is server-rendered every time, not just for the initial page.
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 rows to 1 million and the timing may still be reasonable (roughly the 100k rows timing multiplied by 10 for me). I think you could go up to 2 million rows before hitting the V8 heap limit, but at that point, it probably makes sense to switch to a backend that doesn't read all data into memory.
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 shinyscreenshot package - so it's kind of heavy on R functions and HTML.
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 if(nrow(data) > 1000) then use a JavaScript render function for the expandable row details, otherwise stick with the R function. It would be ideal if I could keep the R function with formatted styling and actionButton when nrow(data) <= 10000) or higher.
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.
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.
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:
Is my interpretation correct - can you reduce the time to generate and render the reactable with an R function for expandable rows? If so, have I missed something or is this functionality not available yet?
Have you come across any examples of a JavaScript render function version of your CRAN Packages demo? My lack of JS really letting me down here... I need to up my game with it!
@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
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)?
@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.
Reacted by BilboBaagins, Liripo, Gard Kroken and Konrad Rudolph
@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 ?
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.
I noticed however that when using the groupby feature, namely with the Reactable.toggleGroupBy the groupby column is empty, unless I use the v8 backend instead of the df backend.
I'm a bit lost in the javascript at https://github.com/glin/reactable/blob/main/srcjs/useGroupBy.js where this is happening to avoid that it does make the group-by column empty in order to make a pull request.
What exactly is an option to show the group text.
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
df[["__state"]] <- listSafeDataFrame(id = sapply(df[[groupedColumnId]], FUN = function(x) sprintf("%s:%s", groupedColumnId, x)), grouped = sapply(df[[groupedColumnId]], FUN = function(x) TRUE))
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 @glin
PR #428 offers a possible alternative for the performance problem in the original example. It adds lazy loading for row details, so R details callbacks only run when a row is expanded, rather than upfront for every row. I tried the original example with lazyDetails = TRUE, and the initial table render appeared instant on my machine.
It isn’t currently compatible with server = TRUE, but it should help in cases where generating row details is the main bottleneck, like the original example.
Activity
glin commentedon Dec 21, 2019
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).
GitHunter0 commentedon May 5, 2021
The server-side rendering would be indeed a fantastic addition to
reactabletimelyportfolio commentedon Jul 14, 2021
This is a good example with
react-table 7. I might have a need for server-side rendering, and if so then will add code and discussion.gofford commentedon Sep 22, 2021
Did you make any progress with this @timelyportfolio ? Server-side rendering is sorely missed in reactable, and really limits scalability.
GitHunter0 commentedon Jun 1, 2022
Hey @glin , have you seen this awesome react-base-table project ? It looks promising.
There is also glide-data-grid, the project which
Streamlitbased on its new dataframe display.daeyoonlee commentedon Nov 17, 2022
One vote for the server side rendering feature. That's why some tables still use DT.
glide-data-grid This looks really good too.
avraam-inside commentedon Dec 7, 2022
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.
glin commentedon Mar 20, 2023
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
V8package (required for server-side data but not a hard dependency):And then set
reactable(server = TRUE)to enable the experimental server-side support in Shiny:Most features are implemented, but there's still some remaining work to do, including:
And shout-out to Ryszard Szymański who pretty much already did this with the impressive Fast Big Data Tables in Shiny table contest submission more than a year ago!
Going into more detail: the default server-side backend is running JavaScript under the hood (via
V8), pretty much the exact same code that runs in your browser for client-side tables. It's similar to the recent Static Rendering feature, except the table is server-rendered every time, not just for the initial page.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
rowsto 1 million and the timing may still be reasonable (roughly the 100k rows timing multiplied by 10 for me). I think you could go up to 2 million rows before hitting the V8 heap limit, but at that point, it probably makes sense to switch to a backend that doesn't read all data into memory.GitHunter0 commentedon Mar 20, 2023
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)?
BilboBaagins commentedon Mar 25, 2023
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
shinyscreenshotpackage - so it's kind of heavy on R functions and HTML.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
if(nrow(data) > 1000)then use a JavaScript render function for the expandable row details, otherwise stick with the R function. It would be ideal if I could keep the R function with formatted styling and actionButton whennrow(data) <= 10000)or higher.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.
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.
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:
Is my interpretation correct - can you reduce the time to generate and render the reactable with an R function for expandable rows? If so, have I missed something or is this functionality not available yet?
Have you come across any examples of a JavaScript render function version of your CRAN Packages demo? My lack of JS really letting me down here... I need to up my game with it!
Thanks again for a brilliant package!
glin commentedon Mar 27, 2023
@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
glin commentedon Mar 27, 2023
@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.
Drwhit commentedon Dec 19, 2023
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?
gadepallivs commentedon Mar 12, 2024
@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 ?
jzadra commentedon Mar 28, 2024
+1 for server side! Though I'm going to give the dev a try.
jwijffels commentedon Apr 9, 2024
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.
I noticed however that when using the groupby feature, namely with the Reactable.toggleGroupBy the groupby column is empty, unless I use the
v8backend instead of thedfbackend.I'm a bit lost in the javascript at https://github.com/glin/reactable/blob/main/srcjs/useGroupBy.js where this is happening to avoid that it does make the group-by column empty in order to make a pull request.
What exactly is an option to show the group text.
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
df[["__state"]] <- listSafeDataFrame(id = sapply(df[[groupedColumnId]], FUN = function(x) sprintf("%s:%s", groupedColumnId, x)), grouped = sapply(df[[groupedColumnId]], FUN = function(x) TRUE))jwijffels commentedon Apr 10, 2024
Not sure if I'm hijacking this thread but as this is all related to server side-rendering which is currently experimental.
Let me add that these stopping conditions also need to be removed https://github.com/glin/reactable/blob/main/R/columns.R#L164-L168 in order to allow custom server-side aggregations.
In my local tests, I'm now changing your code at https://github.com/glin/reactable/blob/main/R/server-df.R to be able use a custom frequency function instead of the default.
flash0926 commentedon Aug 8, 2024
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
@glin
ShixiangWang commentedon Jul 21, 2025
I found that I got a very strange error when use development version with
server = TRUE.Fix DuckDB examples for Shiny, warn on client-in-Shiny misuse
gkroken commentedon Sep 21, 2026
PR #428 offers a possible alternative for the performance problem in the original example. It adds lazy loading for row details, so R details callbacks only run when a row is expanded, rather than upfront for every row. I tried the original example with
lazyDetails = TRUE, and the initial table render appeared instant on my machine.It isn’t currently compatible with
server = TRUE, but it should help in cases where generating row details is the main bottleneck, like the original example.