Interactive Time Series Charts in R with echarts4r

Put real dates on the x-axis: a dated line with a zoom slider, and a theme river of group shares over time

Data Visualization

Put real dates on the x-axis with echarts4r — a dated line with a draggable zoom slider, and a theme river that shows how a total splits among groups over time, all from a familiar pipe workflow. Each chart loads on demand so the page stays fast.

Published

July 9, 2026

Modified

July 9, 2026

TipKey takeaways
  • echarts4r wraps Apache ECharts, a free, open-source JavaScript charting library, in a tidy R pipe — e_charts() starts a chart, e_line() adds a line.
  • A time series puts real dates on the x-axis. Give e_charts() a Date column and echarts4r builds a proper time axis; add e_datazoom() for a draggable zoom slider to focus on any period.
  • A theme river (e_river()) shows how a total divides among groups over time — each coloured band is a group, and its thickness is that group’s share that period.
  • On this page every interactive chart sits behind a static poster you click to load — the heavy chart library only downloads on demand, so the page stays within the mobile performance budget.
  • The static poster is a plain ggplot2 chart, produced by code you can copy and run — the interactive version is the same data, made explorable.

Introduction

You already make time series charts with ggplot2: geom_line(), a Date on the x-axis with scale_x_date(), done. That static chart is perfect for a report or a paper. But a long dated series often begs to be explored — read the exact value on a given day, or zoom into one year of a decade. That is where echarts4r comes in: it brings Apache ECharts — a mature, free, open-source charting library — into R with a pipe workflow that feels familiar if you know the tidyverse.

This lesson builds two dated charts. First a dated line with a zoom slider: two stock indices over several years, with a real time axis you can drag to zoom. Then a theme river, a flowing stacked view that shows how a monthly total splits among a few groups over time. Each is built twice — once as a static ggplot2 poster (the reproducible baseline the page loads first), once as an interactive echarts4r widget you open with a click.

NoteWhy each chart loads on click

An interactive ECharts widget ships about 1 MB of JavaScript and draws to an HTML canvas (the browser’s pixel-drawing surface). Loading several at once would make the page slow on a phone — it would blow the Core Web Vitals budget (Google’s page-speed metrics, of which LCP, the time to paint the largest element, is the one a heavy widget hurts most). So on this page each interactive chart is represented by a static image first; clicking ▶ Load interactive chart swaps in the real, fully interactive echarts4r widget. You get the speed of a static page and the full interactivity on demand. In your own report or Shiny app you would simply print the widget directly — the code is identical.

The static baseline: a dated ggplot2 line

We will chart two European stock indices — the German DAX and the British FTSE — from the built-in EuStockMarkets series. It has no dates of its own, so we attach a sequential daily date to each observation, giving us a real time axis. First, build the data frame with base R and draw both lines with ggplot2. This static figure is the reproducible baseline — and, on this page, the main image the page loads first.

library(ggplot2)

# Attach a daily date to each observation (base R — no dplyr needed)
idx <- data.frame(
  day  = as.Date("1991-01-01") + seq_len(nrow(EuStockMarkets)) - 1,
  DAX  = as.numeric(EuStockMarkets[, "DAX"]),
  FTSE = as.numeric(EuStockMarkets[, "FTSE"])
)

# Long form so ggplot draws one coloured line per index
idx_long <- data.frame(
  day   = rep(idx$day, 2),
  level = c(idx$DAX, idx$FTSE),
  index = rep(c("DAX", "FTSE"), each = nrow(idx))
)

ggplot(idx_long, aes(day, level, colour = index)) +
  geom_line(linewidth = 0.6) +
  scale_colour_manual(values = c(DAX = "#3a86d4", FTSE = "#f5a524")) +
  scale_x_date(date_labels = "%Y") +
  labs(x = NULL, y = "Index level", colour = NULL,
       title = "European stock indices over time") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "top")

Two stock indices, DAX in azure and FTSE in amber, plotted over several years with dates on the x-axis, both trending upward.

The two lines use the brand azure #3a86d4 and amber #f5a524, scale_x_date() formats the axis as years, and theme_minimal() keeps it clean. This is a publication-ready chart. Now let’s make the same series explorable.

A dated line with a zoom slider

echarts4r reads a data frame, you name the x column in e_charts(), then add a line with e_line(). When that x column is a Date, echarts4r builds a time axis automatically — evenly spaced by real calendar distance, with sensible date ticks. Because this is a long series (over 1,800 days), we add a zoom slider with e_datazoom() so the reader can focus on any stretch. We assign the chart to line rather than printing it inline, so the page stays light — then load it behind the poster below.

invisible(Sys.setlocale("LC_TIME", "C"))   # locale-neutral English date labels (axis + poster)
library(echarts4r)

idx <- data.frame(
  day  = as.Date("1991-01-01") + seq_len(nrow(EuStockMarkets)) - 1,
  DAX  = as.numeric(EuStockMarkets[, "DAX"]),
  FTSE = as.numeric(EuStockMarkets[, "FTSE"])
)

line <- idx |>
  e_charts(day) |>                       # a Date column -> a time axis
  e_line(DAX) |>                         # one line series per index column
  e_line(FTSE) |>
  e_color(c("#3a86d4", "#f5a524")) |>    # azure + amber, matching the static chart
  e_tooltip(trigger = "axis") |>         # one tooltip showing both indices at a date
  e_datazoom(type = "slider")            # a draggable zoom slider below the axis

Read the pipe top to bottom: start a chart on day, add a DAX line and a FTSE line, colour them azure and amber, show a shared tooltip on hover, and add the slider. Two e_line() calls give two series — the same base-R, one-call-per-column idiom the line chart lesson uses for grouped data, no dplyr needed. Now save the widget and show it behind a poster.

Two stock indices plotted over several years with dates on the x-axis. Hover a date to compare both indices; drag the slider to zoom into a period.

Hover any date and ECharts shows both index values together; drag the slider handles to zoom into a single year of trading — the interactions you cannot get from a static PNG. Everything else is the same data and the same azure-and-amber palette.

A theme river: shares of a total over time

A theme river (also called a stream graph) shows how a total divides among groups across time. Each coloured band is a group; the band’s thickness is that group’s value in that period, and the bands stack into the overall total. It reads like a flowing area chart and is a natural fit for “how did the mix change over time” questions. echarts4r draws one with e_river().

The base-R idiom mirrors the multi-line pattern: keep the data wide (a date column plus one value column per group) and add one e_river() call per group — no dplyr group_by() needed. Here we use a small fixed table of three groups over six months.

invisible(Sys.setlocale("LC_TIME", "C"))   # locale-neutral English month labels (axis + poster)
library(echarts4r)

# A small wide table: one Date column + one value column per group (base R)
mo <- seq(as.Date("2023-01-01"), by = "month", length.out = 6)
rv <- data.frame(
  month = mo,
  A = c(10, 12, 15, 14, 18, 20),
  B = c( 8,  9,  7, 11, 10, 12),
  C = c( 5,  6,  8,  7,  9, 11)
)

river <- rv |>
  e_charts(month) |>
  e_river(A) |>                              # one river band per group column
  e_river(B) |>
  e_river(C) |>
  e_color(c("#440154", "#21918c", "#fde725")) |>  # viridis, colourblind-safe
  e_tooltip(trigger = "axis")                      # all three shares at the hovered date

Each group gets its own e_river() call on its own column and its own viridis colour. e_tooltip(trigger = "axis") reports all three shares at once when you hover a month. Save it and show the poster.

A theme river showing how a monthly total splits among three groups over time. Each band is a group; its thickness is that group’s share that month.

In the loaded chart, the bands flow and animate, and hovering a month lists every group’s share at once — the kind of exploration that turns a static stack into a story you can read across time.

The dated line plots EuStockMarkets — the DAX and FTSE index levels — with a sequential daily date attached to each of its rows. The theme river uses this small fixed table of three groups over six months:

Monthly values for three groups (A, B, C) — the theme-river data.
month A B C
2023-01-01 10 8 5
2023-02-01 12 9 6
2023-03-01 15 7 8
2023-04-01 14 11 7
2023-05-01 18 10 9
2023-06-01 20 12 11

Common issues

The chart doesn’t appear / the page just shows the poster. That’s by design on this site — click ▶ Load interactive chart and the real widget loads. In your own document, print(line) (or letting line be the last line of a chunk) renders the widget directly with no poster.

My x-axis shows numbers, not dates. echarts4r infers the axis type from the column. A numeric x gives a plain value axis; only a real Date (or POSIXct) column gives a proper time axis with date ticks. Build the x column with as.Date(...) before you pipe it into e_charts().

My theme river only shows one band, or errors. e_river() draws one band per call, from a wide table (a date column plus one value column per group). Add one e_river() per group column, as in the lesson — don’t reach for dplyr’s group_by(). If your data is long (a group column plus a value column), reshape it to wide first with reshape(long, idvar = "date", timevar = "group", direction = "wide").

Frequently asked questions

Yes. echarts4r is released under the Apache 2.0 licence and wraps Apache ECharts, which is also Apache 2.0 — a permissive, free, open-source licence with no per-site or commercial fee. You can use it in reports, dashboards, and public sites without a paid licence.

Make the x column a real Date (or POSIXct) and pass it to e_charts(). echarts4r then builds a time axis automatically — spaced by calendar distance with sensible date ticks. A numeric or character x gives a plain value or category axis instead, so convert with as.Date(...) first.

A theme river (or stream graph) is a stacked, flowing area chart: each coloured band is a group, its thickness is that group’s value over time, and the bands stack to the total. Use it to show how a mix changes over time — market share by brand, traffic by source, budget by category. For an exact value-per-date read it is less precise than separate lines, so pair it with a tooltip.

Keep the data wide — a date column plus one value column per group — and add one e_river() call per column. That is the base-R idiom this lesson uses, and it mirrors adding one e_line() per column for a multi-series line chart. No group_by() required.

ggiraph makes your existing ggplot2 time series interactive by rendering it as SVG, so the look is identical to ggplot2 and the widget is lightweight. echarts4r draws with a different engine (Apache ECharts on an HTML canvas) that offers richer built-in interactions — a draggable zoom slider, a real time axis, animated theme rivers — at the cost of a larger JavaScript payload. Use ggiraph for your publication ggplot look with light interactivity; reach for echarts4r when you need those richer built-in controls.

Test your understanding

Using the built-in AirPassengers series (144 monthly totals from January 1949), attach a real monthly Date to each value, then draw an interactive dated line with echarts4r: a single azure line, a hover tooltip, and a zoom slider. Assign the chart to a variable and print it to view.

Build the date column with seq(as.Date("1949-01-01"), by = "month", length.out = 144) and pair it with as.numeric(AirPassengers) in a data frame. Then chain e_charts(date), e_line(passengers), e_color("#3a86d4"), e_tooltip(), and e_datazoom(type = "slider").

library(echarts4r)

# Base-R data prep: a monthly Date column + the values
ap <- data.frame(
  date       = seq(as.Date("1949-01-01"), by = "month", length.out = 144),
  passengers = as.numeric(AirPassengers)
)

ap |>
  e_charts(date) |>
  e_line(passengers, name = "Passengers") |>
  e_color("#3a86d4") |>
  e_tooltip() |>
  e_datazoom(type = "slider")

Because date is a real Date, echarts4r builds a time axis; the slider lets you zoom into any run of months.

Quick check. You have a wide table with a date column and three group columns, and you want a theme river with a band for each group. How many e_river() calls do you write?

Three — one per group column. e_river() draws a single band per call, reading from a wide table, so three groups means e_river(A) |> e_river(B) |> e_river(C). It is the same one-call-per-column pattern as e_line() for multi-series lines.

Conclusion

echarts4r turns a familiar time series into an explorable one: give e_charts() a Date column for a real time axis, add e_line() and e_datazoom() for a zoomable dated line, or e_river() for a theme river of group shares over time. The static ggplot2 poster keeps the page fast; one click reveals the full interactive chart. Every result on this page was produced by the code shown — copy any block and run it to reproduce them. From here, the same pipe grammar extends to bar, scatter, and many other chart types.

Reuse

Citation

BibTeX citation:
@online{2026,
  author = {},
  title = {Interactive {Time} {Series} {Charts} in {R} with Echarts4r},
  date = {2026-07-09},
  url = {https://www.datanovia.com/learn/data-visualization/echarts4r/interactive-time-series},
  langid = {en}
}
For attribution, please cite this work as:
“Interactive Time Series Charts in R with Echarts4r.” 2026. July 9. https://www.datanovia.com/learn/data-visualization/echarts4r/interactive-time-series.