Reason + BuckleScript bindings to DOM


Experimental bindings to the DOM and other Web APIs.

npm Travis Issues Last Commit

The bindings are currently undocumented, but as the code mostly just consists of external declarations with type signatures, the code itself is fairly self-documenting. The bindings generally also correspond very well to the Web APIs they bind to, so using MDN along with GitHub should go a long way.


npm install bs-webapi

Then add bs-webapi to bs-dependencies in your bsconfig.json. A minimal example:

  "name": "my-thing",
  "sources": "src",
  "bs-dependencies": ["bs-webapi"]


See the examples folder

Please only use the modules exposed through the toplevel module Webapi, for example Webapi.Dom.Element. In particular, don't use the 'flat' modules like Webapi__Dom__Element as these are considered private and are not guaranteed to be backwards-compatible.

Some notes on the DOM API

The DOM API is mostly organized into interfaces and relies heavily on inheritance. The ergonomics of the API is also heavily dependent on dynamic typing, which makes it somewhat challenging to implement a thin binding layer that is both safe and ergonomic. To achieve this we employ subtyping and implementation inheritance, concepts which aren't very idiomatic to OCaml (or Reason), but all the more beneficial to understand in order to be able to use these bindings effectively.


The Dom types, and the relationships between them, are actually defined in the Dom module that ships with bs-platform (Source code), where you'll find a bunch of types that look like this:

  • RE
  • ML
type _element('a);
type element_like('a) = node_like(_element('a));
type element = element_like(_baseClass);
type 'a _element
type 'a element_like = 'a _element node_like
type element = _baseClass element_like

This is subtyping implemented with abstract types and phantom arguments. The details of how this works isn't very important (but see #23 for a detailed explanation of how exactly this trickery works) in order to just use them, but there are a few things you should know:

  • If you expand the alias of a concrete DOM type, you'll discover it's actually a list of abstract types. e.g. element expands to _baseClass _element _node _eventTarget_like This means element is a subtype of _element, _node and _eventTarget_like.
  • The _like type are "open" (because they have a type variable). This means that a function accepting an 'a element_like will accept any "supertype" of element_like. A function accepting just an element will only accept an element (Technically element is actually a "supertype" of element_like too).

This system works exceptionally well, but has one significant flaw: It makes type errors even more complicated than they normally are. If you know what to look for it's not that bad, but unfortunately the formatting of these errors don't make looking for it any easier. We hope to improve that in other ways (see BetterErrors)

Implementation inheritance

If you've looked through the source code a bit, you've likely come across code like this:

  • RE
  • ML
include Webapi__Dom__EventTarget.Impl({ type nonrec t = t });
include Webapi__Dom__Node.Impl({ type nonrec t = t });
include Webapi__Dom__ParentNode.Impl({ type nonrec t = t });
include Webapi__Dom__NonDocumentTypeChildNode.Impl({ type nonrec t = t });
include Webapi__Dom__ChildNode.Impl({ type nonrec t = t });
include Webapi__Dom__Slotable.Impl({ type nonrec t = t });
include Impl({ type nonrec t = t });
  Webapi__Dom__EventTarget.Impl(struct type nonrec t = t[@@nonrec ] end)
include Webapi__Dom__Node.Impl(struct type nonrec t = t[@@nonrec ] end)
include Webapi__Dom__ParentNode.Impl(struct type nonrec t = t[@@nonrec ] end)
                                               type nonrec t = t[@@nonrec ]
include Webapi__Dom__ChildNode.Impl(struct type nonrec t = t[@@nonrec ] end)
include Webapi__Dom__Slotable.Impl(struct type nonrec t = t[@@nonrec ] end)
include Impl(struct type nonrec t = t[@@nonrec ] end)

This is the implementation inheritance. Each "inheritable" module defines an "Impl" module where all its exported functions are defined. include Webapi__Dom__Node.Impl { type nonrec t = t }; means that all the functions in Webapi__Dom__Node.Impl should be included in this module, but with the t type of that module replaced by the t type of this one. And that's it, it now has all the functions.

Implementation inheritance is used instead of subtyping to make it easier to understand which functions operate on any given "subject". If you have an element and you need to use a function defined in Node, let's say removeChild you cannot just use Node.removeChild. Instead you need to use Element.removeChild, which you can since Element inherits from Node. As a general rule, always use the function in the module corresponding to the type you have. You'll find this makes it very easy to see what types you're dealing with just by reading the code.



  • Added URLSearchParams.forEach
  • Added Element.scrollBy and Element.srollByWithOptions


  • (Breaking) Changed DomRect coordinates to use float instead of int


  • Added File.name


  • Added width, height, setWidth and setHeight to CanvasElement


  • Removed deprecated Webapi.Dom.onload function
  • Removed deprecated Webapi.File.Url module alias
  • Restructured internal module layout (non-breaking for modules accessed through the Webapi top-level module, but breaking if internal *Re modules have been accessed directly)
  • Enforce private modules (effectively a formality and non-breaking)


  • Added HtmlElement.focusPreventScroll
  • Refined return type of Node.cloneNode and Node.cloneDeepNode so it now returns the specific type of the cloned node.


  • Added Element.scrollTo, Element.scrollToWithOptions


  • Added URLSearchParams.makeWithDict and URLSearchParams.makeWithArray


  • Added bs.return nullable to URLSearchParams.get since it returns null, not undefined and therefore does not autmatically conform to the runtime representation of option as previosuly assumed.


  • Fixed signature of NamedNodeMap.toArray, which returned element but should return attr (considere non-breaking since it was just plain wrong)
  • Added add... and removePopStateEventListener to Window
  • Added add... and remove... functions for touch and animation event listeners to EventTarget


  • Added add... and remove... functions for drag event listeners to EventTarget


  • (Breaking) Requires bs-platform > 4.0.0
  • (Breaking) Changed FocusEvent.relatedTarget to return option
  • Added HtmlFormElement and HtmlInputElement


  • (Breaking) Fixed return type if StorageEvent.oldValue and StorageEvent.newValue. They should be nullable, but were not.
  • Added Url and UrlSearchParams
  • Deprecated Webapi.File.Url in favor of Webapi.Url


  • EventTarget.dispatchEvent now take a Dom.event_like(_) instead of just Dom.event, so it will accept any event subtype.
  • Window.pageXOffset, pageYOffset, scrollX, scrollY, scrollLeft and scrollTop now return floats instead of ints, and Window.scroll, scrollBy, scrollTo, setScrollLeft and setScrollTop take floats instead of ints
  • HtmlElement.offsetParent now returns an option
  • Selection.anchorNode and Selection.focusNode now return options
  • Element.closest now returns an option


  • Added inheritance of HtmlElement and its ancestors to HtmlImageElement
  • Deprecated HtmlImageElement.onload
  • Fixed inconsistencies with HtmlImageElement.src and HtmlImageElement.getSrc, breaking the API
  • Fleshed out HtmlImageElement


  • Renamed Document.docType to Document.doctype to fix #95


  • Support bs-platform@3.0.0. If your app isn't using that version, then don't upgrade to 0.9.0; otherwise, please do!


  • Added EventTarget.unsafeAsDocument, EventTarget.unsafeAsElement and EventTarget.unsafeAsWindow functions
  • Removed deprecated Bs_webapi module`
  • Added event-specific listener APIs to EventTarget, e.g. EventTarget.addMouseMoveListener(mouseEvent => ...)
  • Added requestCancellableAnimationFrame and cancelAnimationFrame
  • Fixed msising @bs.return annotations causing type unsoundness
  • Fixed typo in encoding of insertPosition type
  • Added Dom.HtmlImageElement, File and File.Url
  • Fixed HtmlElement.offsetParent returning int instead of Dom.Element


  • Added Webapi module, Deprecated Bs_webapi
  • Removed deprecated Storage API
  • Add Document.unsafeAshtmlDocument, Element.unsafeAsHtmlElement. Deprecated Document.asHtmlDocument, Element.asHtmlElement, HtmlEleement.ofElement.
  • Changed Dom.history and Dom.location to use window instead of document


  • Fix incorrect heuristic in HtmlElement.ofElement


  • Renamed createText to CreateTextNode, according to spec
  • Deprecated Storage API, it's been upstreamed to bs-platform as Dom.Storage
  • Removed ReasonJs namespace. Use Bs_webapi instead