A vertical sequence of events.
Usage
el_timeline(
id = NULL,
items = list(),
reverse = FALSE,
html = FALSE,
mode = NULL,
width = NULL,
slots = NULL,
session = NULL
)
update_el_timeline(
session = shiny::getDefaultReactiveDomain(),
id,
items = NULL,
reverse = NULL,
mode = NULL
)Arguments
- id
Timeline ID (auto-generated if NULL).
- items
A list of entries, each an
el_timeline_item()– or a list with the same fields:contentand optionallytimestamp,type("primary","success","warning","danger"or"info"),color,size("normal"or"large"),hide_timestamp,icon(an icon's name),placement("bottom"or"top", where the timestamp goes),center(centre the dot against the content) andhollow(draw the dot hollow).- reverse
Show the entries newest first.
- html
Render each entry's
contentas HTML rather than text – a string of markup, or tags. Only use it with content you control: it goes throughv-html, which does not escape anything.- mode
Relative position of timeline and content. Element Plus's
mode('start' | 'alternate' | 'alternate-reverse' | 'end').- width
Component width, as a CSS unit –
"200px","50%", or a number taken as pixels. Element's own markup carries it, so it behaves like thewidthargument of a Shiny input.- slots
Named list of Element slot contents, such as
list(title = shiny::tags$b("Bold")). A shiny.element component given here is absorbed rather than nested. For a scoped slot, write the template withtemplate().- session
In
el_timeline(), deprecated: inside a module, wrapidinns(), as for any Shiny input; a session given here namespacesidonce more, with a warning. Inupdate_el_timeline(), the Shiny session, the current one by default, as forshiny::updateTextInput().
Details
Entries are rendered with v-for from a data field, so
update_el_timeline() can replace them – useful for a log that grows.
Content given as tags – el_card(...) – is drawn as markup; a string
is text unless html = TRUE.
Updating from the server
update_el_timeline() changes the component from the server.
Every other argument of el_timeline() that can change once it is
drawn is an argument here too, under the same name. One left NULL
stays as it is; NA returns it to Element's default.
update_el_timeline() is called for its side effect and returns NULL invisibly.
Examples
el_timeline(
id = "log",
items = list(
list(content = "Order placed", timestamp = "2026-03-01", type = "primary"),
list(
content = "Order shipped",
timestamp = "2026-03-02",
type = "success",
icon = "el-icon-check",
size = "large"
),
list(content = "Delivered", timestamp = "2026-03-04", color = "#0bbd87")
)
)
#> <div id="log" data-shiny-vue style="display: contents">
#> <script type="text/x-template" data-shiny-vue-template><div id="log_container" style="display: contents">
#> <el-timeline :reverse="reverse" :mode="mode === null ? undefined : mode">
#> <el-timeline-item v-for="(item, index) in items" :key="index" :timestamp="item.timestamp" :type="item.type" :color="item.color" :size="item.size" :icon="item.icon" :placement="item.placement" :center="item.center" :hollow="item.hollow" :hide-timestamp="item.hide_timestamp != null ? item.hide_timestamp : !item.timestamp"><span v-if="item.contentHtml" v-html="item.content"></span><template v-else>{{ item.content }}</template></el-timeline-item>
#> </el-timeline>
#> </div></script>
#> <script type="application/json" data-shiny-vue-options>{"options":{"data":{"items":[{"content":"Order placed","timestamp":"2026-03-01","type":"primary"},{"content":"Order shipped","timestamp":"2026-03-02","type":"success","size":"large","icon":"el-icon-check"},{"content":"Delivered","timestamp":"2026-03-04","color":"#0bbd87"}],"reverse":false,"mode":null}},"input":null,"rate":null,"type":null,"use":["shinyElement.plugin"],"evals":[]}</script>
#> </div>
# Newest first, timestamps above each entry
el_timeline(
id = "log",
reverse = TRUE,
items = list(
list(content = "Second", timestamp = "10:30", placement = "top"),
list(content = "First", timestamp = "09:15", placement = "top")
)
)
#> <div id="log" data-shiny-vue style="display: contents">
#> <script type="text/x-template" data-shiny-vue-template><div id="log_container" style="display: contents">
#> <el-timeline :reverse="reverse" :mode="mode === null ? undefined : mode">
#> <el-timeline-item v-for="(item, index) in items" :key="index" :timestamp="item.timestamp" :type="item.type" :color="item.color" :size="item.size" :icon="item.icon" :placement="item.placement" :center="item.center" :hollow="item.hollow" :hide-timestamp="item.hide_timestamp != null ? item.hide_timestamp : !item.timestamp"><span v-if="item.contentHtml" v-html="item.content"></span><template v-else>{{ item.content }}</template></el-timeline-item>
#> </el-timeline>
#> </div></script>
#> <script type="application/json" data-shiny-vue-options>{"options":{"data":{"items":[{"content":"Second","timestamp":"10:30","placement":"top"},{"content":"First","timestamp":"09:15","placement":"top"}],"reverse":true,"mode":null}},"input":null,"rate":null,"type":null,"use":["shinyElement.plugin"],"evals":[]}</script>
#> </div>
if (interactive()) {
# Append an entry to a growing log
observeEvent(input$refresh, {
log_entries(c(
log_entries(),
list(list(content = "Refreshed", timestamp = format(Sys.time(), "%H:%M")))
))
update_el_timeline(session, "log", items = log_entries())
})
}