I think there are a couple of ways to do this. First off, I would say this is a case for functors - except that it isn’t because you’d have to write an unreasonable amount of bindings for the plugins.
If it wasn’t plugins but plain-old-data, you could probably use array<Js.Json.t>.
I think your only option here is to say remarkPlugins={[/*plugins here*/]->Obj.magic}
Which actually kind of sucks, and this is something I find myself doing way too often. It’s not actually a problem with ReScript, but with JS legacy. These horrible quirks you keep running into with ReScript is basically the insanity that is JS being exposed. It sucks, but you have to put up with it, or move to Rust/OCaml. There’s no way to process a heterogenous array of elements in a statically typed environment unless it’s fixed length (meaning compile-time fixed length).
You can benefit from the fact that remark plugins are functions, options are objects (I defined it as an inline record here but you could replace it with Dict.t<JSON.t> if you want to be more generic), so all variant cases are allowed to be unboxed.
We could go a step further (and safer) by allowing tuples in unboxed variants, but it’s not the case yet, so it has to be a regular array for now (which requires the definition of an additional variant).