14 The reactive graph
14.1 Introduction
Objectives and chapter section list
The main objective of this chapter is to understand more fully and in depth the reactive graph.
- Understanding the reactive graph
- Importance of invalidation
- Using the {reactlog} package
14.2 A step-by-step tour of reactive execution
14.2.1 Using a real app
Instead of the imaginary demo app from the book I will start my own tour on reactive execution with a much simpler but very useful app. Using the diamonds dataset of {ggplot2} we’ll explore the data with bar charts of the different variables in the dataset.
Using a different app than in the book results in a different reactlog
tour and structure of this chapter. The book uses a nonsensical app claiming that the underlying real app is not important for the understanding. The following step-by-step tour explains the different app states with the modelling of artificially created abstracts graphics. Only at the end of the chapter is a short section introducing the {reactlog} package.
In contrast to this approach I believe that a real life example in a real live environment is important. Therefore I will reverse the approach: My central focus is to understand the reactlog
of a real app using the {reactlog} package. Instead of abstract graphics I will interact with the app and display the resulting reactlog
via screenshots of the different steps1.
R Code 14.1 : Exploring diamonds
data with bar charts
diamonds
data with bar charts
Let me comment some details of the app:
- With the exception of shiny functions, I have used as always the
<package-name>::
convention instead of thelibrary()
function. This is helpful for me to understand and remember the package origin of the function. - I have set the theme from the gray background standard to a white background. This is not relevant for the functioning of the app but results in my opinion to a better visible output.
- As the variable (
carat
) is stored inside another variable (input$var
), I have solved — following Chapter 12 — the indirection problem in the data-masking function withggplot2::aes(.data[[input$var]])
.
14.2.2 Preparing for the reactlog
Drawing the reactive graph by hand is a powerful technique to help you understand simple apps and build up an accurate mental model of reactive programming. But it’s painful to do for real apps that have many moving parts. Wouldn’t it be great if we could automatically draw the graph using what Shiny knows about it? This is the job of the {reactlog} package, which generates the so called reactlog
which shows how the reactive graph evolves over time.
Procedure 14.1 : Using the {reactlog} package
- Install the {reactlog} package
- Turn logging with
reactlog::reactlog_enable()
on. (But to enablereactlog
, you could also setoptions(shiny.reactlog = TRUE)
before running the application. But I prefer the first option which is recommended in the newer documentations.) - Start the app.
From here you have two options:
-
Live inspection during runtime: While the app is running, press Cmd-F3Cmd-F3 (or Ctrl-F3Ctrl-F3 on Windows), to show the
reactlog
generated up to that point. You can interact with the app and (after refreshing your browser) inspect live the resulting status changes of the app. (Important: Calling thereactlog
only works after you have interacted at least once with the app. Otherwise the keyboard shortcut will not open your default browser with a new tab and generate thereactlog
.) -
Post mortem analysis: After the app is closed you can inspect the full
reactlog
graph for all sessions withshiny::reactlogShow()
. The function opens a static HTML in your default browser. In this case, refreshing your browser will not load new activity into the report; you will need to callshiny::reactlogShow()
explicitly.
In Listing / Output 14.2 contains the same R code as in Listing / Output 14.1 with the important addition of reactlog::reactlog_enable()
to turn on reactive logging. Note that reactlog::reactlog_enable()
is one of the few (in total eight) functions of the {reactlog} package. Most of the commands to control the reactlog
are Shiny functions.
R Code 14.2 : Turning on reactive logging for the diamonds
exploring app
diamonds
exploring app
14.2.3 Setting up the reactlog
environment
What follows are all the shiny steps that are done immediately after startup of our diamonds
exploration app. I will stop the app instantly after startup and call shiny::reactlogShow()
(because the keyboard shortcut for live inspections does not work at this stage). So there was no interaction with the app besides starting and closing.
Between the hand made graphics in chapter 14 of Mastering Shiny or in the Shiny Reactlog article and the reactlog
there are several differences to note. Although reactlog
uses the same graphical conventions as the mentioned text references, there are nonetheless some important distinctions:
- The biggest difference is that
reactlog
draws every dependency, even if it’s not currently used, in order to keep the automated layout stable. Connections that are not currently active (but were in the past or will be in the future) are drawn as thin dotted lines. Therefore there are not only dotted lines but also dark and light gray objects. Dark gray means that the reactives are in the process of invalidating, whereas light gray denotes they are already invalidated. - Another important difference is that
reactlog
shows also three additional reactive inputs (clientData$output_x_height
,clientData$output_x_width
, andclientData$pixelratio
) that don’t appear in the source code. These additional reactive inputs exist because plots have an implicit dependency on the size of the output; whenever the output changes size the plot needs be redrawn. This addition is annoying because it makes thereactlog
much more complex that without it. For instance the startup procedure without these additional inputs would be five instead of 18, and after one interaction 44 instead of 12 steps! - In the
reactlog
there is also withTheme Counter
another reactive input. The documentation about this addition is sparse. But I found finally out that it is created by thereactlog::reactlog_enable()
function. It is areactiveVal()
that is used to track changes in the current theme of a Shiny session when (the not documented)shiny::setCurrentTheme()
function is called2. This counter helps in managing dependencies related to Shiny’s Bootstrap theme changes within the reactive graph of a Shiny application. From a StackOverflow answer I learned that this function does three things: (1) It sets theme as the current bootstrap theme, (2) re-executes any registered theme dependencies, and (3) sends the resulting dependencies to the client. - Another difference is that in the standard mode the
reactlog
shows also the time it takes for calculating the output.
14.2.3.1 What do the different symbols mean?
- The objects of the left side represent reactive inputs or reactive values. For example this here
is a ready input.
- The object(s) in the middle of the
reactlog
designate reactive expression. For example this hereis an invalidated reactive expression.
- The objects on the right hand side are (with the exception of the
Theme counter
) outputs or observers. Anywhere you see “output”, you can also think “observer.” The primary difference is that certain outputs that aren’t visible will never be computed. We’ll discuss the details in Section 15.3. For example this hereis an output or observer just in the process of invalidating.
The lines between the shapes are directional, with the arrows indicating the direction of their inter-dependencies. When a shiny app is loaded, the graph in Picture 14.1 has to be discovered and formed. Because of the additional three plot reactives (height, width and ratio) Shiny need 21 steps to find and rebuild all the elements of the app.
Recall that reactive inputs and expressions are collectively called reactive producers; reactive expressions and outputs are reactive consumers.
The covered the colors in the examples above of the reactlog
objects lack one additional color: Orange means that the object is just in the process of calculating a new value.
What you see in Picture 14.1 is step 21 reached after the startup of the diamonds
exploring app. The reactlog
is always starting with the first idle state after the startup. If you interact with the app extensively and type the keyboard F3 shortcut, then Shiny opens the default browser window displaying the first idle state of the startup, independently how many interactions and idle states you have had.
14.2.3.2 Labelling reactive expressions and observers
Note that while reactive inputs and outputs have names, reactive expressions and observers do not, so they’re labelled with their contents. In this case it is not important because the reactive has a short name and there are not many objects sharing the screen space. But reactives can be long strings abridged by reactlog
and/or interfering with other reactlog
objects. To make things easier to read (and understand) you may want use the label
argument to reactive()
and observe()
, which will then appear in the reactlog
. You can use emojis to make particularly important reactives stand out visually.
The label argument works only with reactive()
or observe()
. I have therefore our hello-reactlog app so constructed that it has one reactive expression. This wouldn’t be necessary for this simple app, because all computation could be done in renderPlot()
output.
R Code 14.3 : Demonstrating how to highlight reactives using name and emoji in their label
argument
label
argument to highlight a reactive by name and emoji
gg
object and displays a bar chart emoji for the gg
reactive
By pure change I learned that plotting the diamonds
bar charts with the {plotly} package prevents the annoying complexity with the three reactives. I do not understand why there are no plot objects reactives with Plotly. Probably it has to do with the different {plotly) functions used instead of the original Shiny functions:
-
plotly::plotlyOutput()
instead ofshiny::plotOutput()
, -
plotly::renderPlotly()
instead ofshiny::renderPlot()
and -
plotly::plot_ly()
instead of several {gplot2} function.
Resource 14.1 : Plotly resources
- Online book: Interactive web-based data visualization with R, plotly, and shiny (especially chapter 2).
- Online Tutorials: Getting Started with Plotly in R
- Graphic Library: Plotly R Open Source Graphing Library
- My notes: Learning Plotly (especially chapter 2).
Unfortunately I can’t remember where I found the code of Listing / Output 14.4 in Hello Reactlog. This demo real functional demo app is a good occasion to learn about the working of Shiny apps without disturbing and irrelevant steps in the reactlog
. I changed the code found in “Hello Reactlog” in two ways:
- I created a reactive function where all the computation is done whereas
renderPlotly()
only prints the resulting bar charts. - I added
type = "histogram"
to theplotly::plot_ly()
function to prevent the following annoying message:
No trace type specified: Based on info supplied, a ‘histogram’ trace seems appropriate. Read more about this trace type -> https://plotly.com/r/reference/#histogram
R Code 14.4 : Using {plotly} to prevent irrelevant plot objects reactives
14.2.3.2.0.1 Package version mismatch with the WebAssembly version of shinylive-r
for {plotly}
I got the following warning message:
Package version mismatch for plotly, ensure the versions below are compatible.
ℹ Installed version: 4.11.0, WebAssembly version: 4.10.4.
ℹ Install a package version matching the WebAssembly version to silence this error.
But fortunately it seems that {plotly} is working, even if there is a version mismatch.
When a user interacts with the app and changes reactive input values, the graph is partially deconstructed and then reformed. The reactlog
visualization is designed to help you inspect each particular step in this reactive life cycle. The sub-sections that follow explain how this reactive life cycle works in detail.
For the following analysis I set the reactlog up with three steps:
- I start the R Code 14.4 app. After startup has finished the app has drawn the bar chart with the variable
carat
. - I choose variable
cut
for another bar chart. And the app shows me the correspodning bar chart. - Finally I pressed Cmd-F3Cmd-F3. The program changes from RStudio to my default browser Brave and displays the reactlog.
14.3 Analyzing the reactlog
14.3.1 A first look at the reactlog
As I said before the reactlog
displays its first idle state after startup. This is for our app the step number eight.
There are several steps before and after our starting point. This is the right time to make yourself familiar with the handling of the reactlog visualization through reading Reactlog in detail. It has documented all the navigation elements and much more. Many nice feature of the reactlog
are hidden and can be assesses only via keyboard shortcuts. The article explains
- the status bar with all its elements
- the progress bar,
- the information displayed during the current step,
- the navigation buttons,
- the search bar and
- the handling of the dependency graph with
- the possible reactive states of the objects or nodes, reflected by their colors,
- the highlighting and filtering,
- the zooming
- the labeling of the nodes
Under the section Hello Reactlog you will find a video with exactly the same steps I am going to explain in the following sub-sections. To get an overview what I am going to explain or to see the context of the individual steps explained below I have included the video here:
The video starts with the carat
bar chart followed by the color
bar charts. Around second four the user hit the Cmd-F3Cmd-F3 shortcut to get the reactlog
view. I will take the information in The reactive cycle section and adapt it to our real world app about exploring the diamonds
dataset using {plotly}.
14.3.2 A session begins
When you load a shiny app, all reactive expression and observers begin in an invalidated state (indicated in gray). Invalidated state essentially means that any code that the node depends upon needs to be re-executed.
14.3.3 Execution begins
After initializing a session, Shiny picks an observer (not a reactive expression) and starts executing it. Reactlog
uses an orange fill to indicate when a node is actively executing (i.e., calculating).
You may wonder how Shiny decides which of the several invalidated observers/outputs to execute. The short answer is that you should act as if it’s random. Ideally, your observers and especially outputs won’t care what order they execute in, as each will function independently from the others. However, if you do have observers whose side effects must always happen in a certain relative order, you can use the observe function’s priority
argument to dictate that order for observers. You can also control the priority of outputs by calling the outputOptions()
function with a priority
argument.
14.3.4 Detecting dependency
During an observer’s execution, it looks for it’s dependencies which one or more reactive expressions. As soon as the dependency has detected, the observer becomes dependent on the reactive expression, represented below by the new arrow.
14.3.5 Reading a reactive expression
Remember that all reactive expressions start out in invalidated state (represented by the gray fill), including the one this observer is now trying to access. In order to return a value, the reactive expression needs to execute its code, which it starts doing now. The reactive expression is filled with orange to represent that it’s running.
Note that the observer is still orange: just because the reactive expression is now running, doesn’t mean that the observer has finished. The observer is waiting on the reactive expression to return its value so its own execution can continue, just like a regular function call in R.
14.3.6 Reading an input
This particular reactive expression happens to read a reactive input. Therefore, the reactive expression depends on the input, represented below by the arrow.
Unlike reactive expressions and observers, reactive inputs simply hold a value, so they don’t require an execution phase (i.e., their value is ready from the start). Therefore the fill with green immediately.
14.3.7 Reactive expression completes
In more complex apps, the reactive expression reads another reactive expression, which in turn reads another input. Those steps are not of interest any more since they’re just a repeat of what we’ve already described. Therefore we have chosen a more simple app so we could continue with the next new and interesting steps.
When the reactive expression has completed executing, it saves (caches) the resulting value internally before returning it to the observer that requested it. Now that the reactive expression has finished executing, it’s no longer in invalidated (gray) or running (orange) state; rather, it’s in idle/ready (green) state. When a reactive expression reaches this state, it means it’s up-to-date and will not re-execute its code even if other reactive expressions or observers request its value. Instead, it can instantly return the value it cached during its most recent execution.
14.3.8 Observer completes
Now that the reactive expression has returned its value to the observer, the observer can complete executing its code. When this has completed, it too enters the idle state, so its fill color will change to green.
14.3.9 Repeated cycle
After Shiny has completed execution of the first observer, it chooses in a more complex app as ours a second one to execute. Again, it turns orange, and may read values from reactive expressions, which will turn orange, and so on. This cycle will repeat until every invalidated observer enters the idle/ready (green) state.
14.3.10 Execution completes, outputs flushed
At last, all of the observers have finished execution and are now idle. This round of reactive execution is complete, and nothing will happen with this session until some external force acts on the system (e.g. the user of the Shiny app choosing another variable in the user interface). In reactive terms, this session is now at rest.
14.3.11 Generalizing for more complex apps
Let’s stop here for just a moment and think about what would have happened with a more complex app. In that case we would read more than one inputs, calculated some values, and generated some outputs. But more importantly, in the course of doing that work, we also have discovered the relationships between these different calculations and outputs. An arrow from a reactive input to a reactive expression tells us that if the reactive input’s value changes, the reactive expression’s result value can no longer be considered valid. And an arrow from a reactive expression to an output means that if the reactive expression’s result is no longer valid, then the output’s previous result needs to be refreshed.
Just as important: In a more complex app we would also know which nodes are not dependent on each other. If no path exists from a particular reactive input to a particular output (always traveling in the direction that the arrows are pointing), then a change to that input couldn’t possibly have an effect on that output. That gives us the ability to state with confidence that we don’t need to refresh that output when that input changes, which is great – the less work we need to do, the sooner we can get results back to the user.
14.3.12 An input changes
Step 8 left off with our Shiny session in a fully idle state. Now remember that we have selected in the application another variable for the bar chart. This causes the browser to send a message to their server, instructing Shiny to update the corresponding reactive input.
When a reactive input or value is modified, it kicks off an invalidation phase, which we haven’t seen up to this point. The invalidation phase starts at the changed input/value, which will be filled with dark gray, the color for starting the invalidation.
14.3.13 Notifying dependents
Now, we follow the arrows that we drew earlier. Each reactive expression and observer that we come across is put into invalidated state, then we continue following the arrows out of that node. As a refresher, for observers, the invalidated state means “should be executed as soon as Shiny gets a chance”, and for reactive expressions, it means “must execute the next time its value is requested”.
In this diagram, the arrows in the lighter shade indicate the paths we took from the changed reactive input through the reactive graph. Note that we can only traverse the arrows in their indicated direction; it’s impossible to move from a reactive expression leftwards to a reactive input, for example.
The procedure just described needs several intermediate steps corresposning to the complexity of the app. Here we have just two steps:
14.3.13.1 Invalidating reactive expression p
14.3.13.2 Invalidating output$plot
14.3.14 Removing relationships
Next, each invalidated reactive expression and observer “erases” all of the arrows coming in or out of it. You can think of each arrow as a one-shot notification that will fire the next time a value changes. Not every time, just the next time. So all of the arrows coming out of a reactive expression are safe to erase; like a used bottle rocket, they’ve fired their one shot.
It may seem perverse that we put so much value on those relationships, and now we’re going out of our way to erase them! But the truth is, though these particular arrows were important, they are now themselves out of date. The only way to ensure that our graph stays accurate is to erase arrows when they become stale, and let Shiny rediscover the relationships around these nodes as they re-execute.
This marks the end of the invalidation phase.
In the hand drawings all relationships are removed, but in the reactlog
the arrows change their color from dark to light gray and also become thinner. Again this procedure requires some intermediate steps in more complex apps. Here we have only two steps to clear all invalidated relations.
14.3.14.1 output$plot
removes dependency on p
14.3.14.2 p
removes dependency on input$var
14.3.15 Re-execution
Now we’re in a pretty similar situation to when the Shiny session first started; we have some invalidated reactive expressions and outputs, and we don’t have any arrows coming in or out of them. It’s time to do exactly what we did then: execute the invalidated outputs/observers, one at a time.
What’s different this time, though, is that — in a more complex app — not all of the reactive expressions and outputs are starting out in the invalidated state. Some parts of the graph weren’t affected (neither directly nor indirectly) by the reactive input that had changed. That’s great, as we won’t need to re-execute those parts of the graph, even if they are used again by some of the invalidated parts!
For all the necessary intermediate steps I will just mention the step description in the reactlog
.
14.3.15.1 output$plot
started (again) calculating
14.3.15.2 output$plot depeneds (again) on p
14.3.15.3 p
started (again) calculating
14.3.15.4 p
depends (again) on input$var
14.3.15.5 p
stopped caclulating
When the calculation process is finished the nodes chance it colors to green.
14.3.15.6 outplut$plot
stopped calculating
14.4 Shiny app in idle state (again)
We have finished the recalculation. All nodes are ready and the app is in idle state again.
14.4.1 Exercises
14.4.1.1 Sum-Prod-Division
Draw the reactive graph for the following server function and then explain why the reactives are not run.
The server function in Listing / Output 14.5 contains only reactive expression but no output. Therefore the reactives are not run.
14.5 Dynamism
Shiny “forgets” the connections between reactive components that it spend so much effort recording. This makes Shiny’s reactive dynamic, because it can change while your app runs.
R Code 14.5 : Example to demonstrate Shiny’s dynamism
You might expect the reactive graph to look like Picture 14.27.
But because that Shiny dynamically reconstructs the graph after the output has been invalidated it actually looks like either of the graphs in Picture 14.28, depending on the value of input$choice
. This ensures that Shiny does the minimum amount of work when an input is invalidated. In this, if input$choice
is set to “b”, then the value of input$a
doesn’t affect the output$out
and there’s no need to recompute it.
It’s worth noting (as Yindeng Jiang does in their blog) that a minor change will cause the output to always depend on both a and b:
Code
output$out <- renderText({
a <- input$a
b <- input$b
if (input$choice == "a") {
a
} else {
b
}
})
This would have no impact on the output of normal R code, but it makes a difference here because the reactive dependency is established when you read a value from input
, not when you use that value.
14.6 Glossary Entries
term | definition |
---|---|
Bootstrap | Bootstrap is a free and open-source CSS framework designed for responsive, mobile-first front-end web development. The framework provides HTML, CSS, and JavaScript-based design templates for various interface components such as typography, forms, buttons, navigation, and more. It aims to simplify the process of creating responsive and mobile-friendly websites by offering pre-styled components and a grid system. |
emoji | Emojis are small digital images or icons used to express emotions, ideas, or concepts in electronic communication, such as text messages, social media posts, and emails. The word "emoji" combines two Japanese words – "e," meaning "picture," and "moji," meaning "character" They originated from simple emoticons, which conveyed emotions in text-based communication. Emojis have become essential to online communication, allowing users to add tone and personality to their messages. |
Reactive consumers | Reactive consumers are objects int the reactive programming coding style of Shiny that depend on the values provided by reactive producers. They include reactive expressions and outputs, which update automatically when their dependencies change. |
Reactive producers | Reactive producers are objects int the reactive programming coding style of Shiny that generate or provide data, such as user inputs or expressions. |
WebAssembly | WebAssembly (also known as WASM) allows software that’s normally compiled for a specific computer system to instead run anywhere, including in web browsers. It is a binary instruction format for a stack-based virtual machine. |
Session Info
Session Info
Code
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.5.1 (2025-06-13)
#> os macOS Sequoia 15.5
#> system aarch64, darwin20
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz Europe/Vienna
#> date 2025-07-17
#> pandoc 3.7.0.2 @ /opt/homebrew/bin/ (via rmarkdown)
#> quarto 1.8.4 @ /usr/local/bin/quarto
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> cli 3.6.5 2025-04-23 [1] CRAN (R 4.5.0)
#> commonmark 2.0.0 2025-07-07 [1] CRAN (R 4.5.0)
#> curl 6.4.0 2025-06-22 [1] CRAN (R 4.5.0)
#> dichromat 2.0-0.1 2022-05-02 [1] CRAN (R 4.5.0)
#> digest 0.6.37 2024-08-19 [1] CRAN (R 4.5.0)
#> evaluate 1.0.4 2025-06-18 [1] CRAN (R 4.5.0)
#> farver 2.1.2 2024-05-13 [1] CRAN (R 4.5.0)
#> fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.5.0)
#> glossary * 1.0.0.9003 2025-06-08 [1] local
#> glue 1.8.0 2024-09-30 [1] CRAN (R 4.5.0)
#> htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.5.0)
#> htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.5.0)
#> jsonlite 2.0.0 2025-03-27 [1] CRAN (R 4.5.0)
#> kableExtra 1.4.0 2024-01-24 [1] CRAN (R 4.5.0)
#> knitr 1.50 2025-03-16 [1] CRAN (R 4.5.0)
#> lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.5.0)
#> litedown 0.7 2025-04-08 [1] CRAN (R 4.5.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.5.0)
#> markdown 2.0 2025-03-23 [1] CRAN (R 4.5.0)
#> R6 2.6.1 2025-02-15 [1] CRAN (R 4.5.0)
#> RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.5.0)
#> rlang 1.1.6 2025-04-11 [1] CRAN (R 4.5.0)
#> rmarkdown 2.29 2024-11-04 [1] CRAN (R 4.5.0)
#> rstudioapi 0.17.1 2024-10-22 [1] CRAN (R 4.5.0)
#> rversions 2.1.2 2022-08-31 [1] CRAN (R 4.5.0)
#> scales 1.4.0 2025-04-24 [1] CRAN (R 4.5.0)
#> sessioninfo 1.2.3 2025-02-05 [1] CRAN (R 4.5.0)
#> stringi 1.8.7 2025-03-27 [1] CRAN (R 4.5.0)
#> stringr 1.5.1 2023-11-14 [1] CRAN (R 4.5.0)
#> svglite 2.2.1 2025-05-12 [1] CRAN (R 4.5.0)
#> systemfonts 1.2.3 2025-04-30 [1] CRAN (R 4.5.0)
#> textshaping 1.0.1 2025-05-01 [1] CRAN (R 4.5.0)
#> vctrs 0.6.5 2023-12-01 [1] CRAN (R 4.5.0)
#> viridisLite 0.4.2 2023-05-02 [1] CRAN (R 4.5.0)
#> xfun 0.52 2025-04-02 [1] CRAN (R 4.5.0)
#> xml2 1.3.8 2025-03-14 [1] CRAN (R 4.5.0)
#> yaml 2.3.10 2024-07-26 [1] CRAN (R 4.5.0)
#>
#> [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/library
#> [2] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
#> * ── Packages attached to the search path.
#>
#> ──────────────────────────────────────────────────────────────────────────────
I am using the term
reactlog
in two different ways: {reactlog} references the package andreactlog
or just reactlog without any emphasis is the result, which shows how the reactive graph evolves over time.↩︎There is for Shiny developers the function
shiny::getCurrentTheme()
to create Shiny bindings with intelligent styling based on theshiny::bootstrapLib()
’s theme value.↩︎