Highcharts, do.call and c(list())

Getting around Error: All arguments must be named list, and why does do.call() want c(list(), list(…)) with htmlwidgets?
R
highcharter
meta-programming
non-standard-evaluation
Author

Charlotte Jane Hadley

Published

December 10, 2025

Highcharts, do.call and c(list())

I love the {highcharter} package. I think it makes some of the most beautiful, engaging and accessible data visualisations amongst all the {htmlwidgets} packages that I use. It’s very customisable! But if you’re like my clients at SafeLives when you try do advanced things with the package you might get stuck on this problem:

Error: All arguments must be named list

This is just one example of where we need to introduce do.call() to provide a list of arguments to a function that get executed as if the function was called directly with those arguments.

Oooft. That gets into meta-programming and non-standard evaluation. Which gets slightly complicated by using htmlwidgets objects.

A {highcharter} motivating example

Let’s make an example chart showing how many respondents fall in each marital status from the gss_cat dataset:

Code
library("highcharter")
library("tidyverse")

hc_marital_bar_chart <- gss_cat |> 
  count(marital) |> 
  hchart(
    type = "bar",
         hcaes(x = marital,
               y = n)
  )

hc_marital_bar_chart

Now imagine that we want to customise the x-axis within a function dependent on several arguments and data variables. Let’s say we want to customise the following:

  • label styles

  • category ordering

In realistic settings we’d likely have a lot more customisation than this.

list_hc_xaxis_args <- list(
  labels = list(
    style = list(fontSize = "20px",
                 color = "red"),
    autoRotation = FALSE
  ),
  categories = c("Married", "Divorced", "Separated", "Widowed", "Never Married", "No answer")
)

Great! But the hc_xAxis() function will choke on that:

hc_marital_bar_chart |> 
  hc_xAxis(list_hc_xaxis_args)
# Error: All arguments must be named list

do.call() to the rescue

This is exactly the problem `do.call() from base R is designed for. Let me show you it in use and then explain it:

do.call(
  hc_xAxis,
  c(list(hc_marital_bar_chart), list_hc_xaxis_args)
)

Here’s the template of the function, do.call(what, args)

  • what is the function we want to pass arguments, hc_xAxis() in our case.

  • args is a list of arguments that are provided to the function.

I think the documentation for do.call() is somewhat esoteric, but our motivating example allows us to see a complexity!

Why on earth are we using c(list(), list(…))?

c() and {htmlwidgets}

What is a {highcharter} object?

class(hc_marital_bar_chart)
[1] "highchart"  "htmlwidget"

Really under the hood the object is a list of lists, but it has the class "highchart". What about list_hc_xaxis_args?

class(list_hc_xaxis_args)
[1] "list"

Do you know what the c() function does? Maybe not. Particularly if you’re vibe coding and/or haven’t really used Base R. The c() function is a lot more clever than it looks, and back in 2015 when I first learned R I often read StackOverflow questions and blog posts that depended on it in some way. Let’s make a fake "highchart" object and use it in c()

fake_hc_object <- structure(list(hc_opts = list("stuff")), class = c("highchart", 
"htmlwidget"), package = "highcharter")

c(fake_hc_object) |> dput()
list(hc_opts = list("stuff"))

That’s not a “highchart” object anymore. That is why we need to contain it within list() to preserve the structure of the object.

c(list(fake_hc_object)) |> dput()
list(structure(list(hc_opts = list("stuff")), class = c("highchart", 
"htmlwidget"), package = "highcharter"))

That’s why this fails. It’s not because the hc argument isn’t named. It’s because of type coercion - the actual thing that c() does.

do.call(
  hc_xAxis,
  c(hc = hc_marital_bar_chart, list_hc_xaxis_args)
)
# Error in (function (hc, ...)  : argument "hc" is missing, with no default

Base R and reprex

Do you know what we did here? We made a REPREX. We reduced the problem down to its bare components. Without using a taskforce of agents that will lovingly hallucinate about how !!! from {rlang} will solve all your problems.

This is one of my favourite things to do as a data coach. Throw away all the complexity. Make toys. Break the toys. Build up your knowledge.

If you want to learn more about how to make a great reprex with R, get a coffee and read this StackOverflow thread without getting. summary.