Skip to content

Examples

The best example of usage of these bindings are the tests.
These typically contain tweaks made to the generated bindings to make them more ergonomic.

AddEventListener__test

let button = DomGlobal.document->Document.querySelector("button")->Null.toOption
let h2 = DomGlobal.document->Document.querySelector("h2")->Null.toOption
switch (button, h2) {
| (Some(button), Some(h2)) =>
button->Element.addEventListener(EventTypes.Click, (e: UiEventsTypes.mouseEvent) => {
Console.log(`Button clicked, ${Int.toString(e.button)}`)
switch h2.textContent {
| Null => h2.textContent = Value("1")
| Value(text) =>
switch Int.fromString(text) {
| None => h2.textContent = Value("1")
| Some(number) => h2.textContent = Value(Int.toString(number + 1))
}
}
})
| _ => Console.log("Stuff not found")
}

AudioDestinationNode__.test

let ctx = AudioContext.make()
let destinationNode = ctx.destination->AudioDestinationNode.asAudioNode
let context = AudioContext.asBaseAudioContext(ctx)
let osc = OscillatorNode.make(
~context,
~options={
type_: WebAudioTypes.Sine,
frequency: 440.0,
},
)
let gain = GainNode.make(
~context,
~options={
gain: 0.3,
},
)
let _ = gain->GainNode.connect(~destinationNode)
let _ =
osc
->OscillatorNode.asAudioScheduledSourceNode
->AudioScheduledSourceNode.connect(~destinationNode=gain->GainNode.asAudioNode)
osc->OscillatorNode.start

DOMMatrix__test

let _make = DOMMatrix.make()
let _fromString = DOMMatrix.fromString("matrix(1, 0, 0, 1, 0, 0)")
let _fromArray = DOMMatrix.fromArray([1., 0., 0., 1., 0., 0.])

DOMMatrixReadOnly__test

let _make = DOMMatrixReadOnly.make()
let _fromString = DOMMatrixReadOnly.fromString("matrix(1, 0, 0, 1, 0, 0)")
let _fromArray = DOMMatrixReadOnly.fromArray([1., 0., 0., 1., 0., 0.])

Element__test

external myElement: DomTypes.element = "myElement"
switch myElement->Element.getAttribute("foo") {
| Null.Value(value) => value->Console.log
| Null => "nothing"->Console.log
}

Fetch__test

let response = await Fetch.fetch("https://rescript-lang.org/")
let response2 = await Fetch.fetch(
"https://rescript-lang.org/",
~init={
headers: HeadersInit.fromDict(
dict{
"Content-Type": "application/json",
"Authorization": "Bearer token",
},
),
body: BodyInit.fromString(`secret=foo&response=bar`),
},
)
let response3 = await Fetch.fetchWithRequest(
Request.fromURL("https://rescript-lang.org/"),
~init={
method: "POST",
headers: HeadersInit.fromDict(
dict{
"Content-Type": "application/x-www-form-urlencoded",
},
),
body: BodyInit.fromString(`secret=foo&response=bar`),
},
)
DomGlobal.removeEventListener(
EventTypes.Mousedown,
MouseEvent.preventDefault,
~options={capture: false},
)
let registrationResult = await Window.current
->Window.navigator
->Navigator.serviceWorker
->ServiceWorkerContainer.register("/sw.js")
let subscription = await registrationResult.pushManager->PushManager.subscribe(
~options={
userVisibleOnly: true,
applicationServerKey: ApplicationServerKey.fromString("MyPublicKey"),
},
)
let pushSubscriptionJSON = subscription->PushSubscription.toJSON
let (auth, p256dh) = switch pushSubscriptionJSON.keys {
| None => ("?", "?")
| Some(keys) => (keys.auth, keys.p256dh)
}
Console.log(`For subscription ${subscription.endpoint}, auth is ${auth} and p256dh is ${p256dh}`)

FontFace__test

let dataView: DataView.t = Obj.magic()
let arrayBuffer: ArrayBuffer.t = Obj.magic()
let _fromString = FontFace.fromString(~family="Inter", ~source="url(/fonts/inter.woff2)")
let _fromDataView = FontFace.fromDataView(~family="Inter", ~source=dataView)
let _fromArrayBuffer = FontFace.fromArrayBuffer(~family="Inter", ~source=arrayBuffer)

FormData__test

/* This works when your form has an id of "myForm" */
@scope(("document", "forms"))
external myForm: HTMLFormElement.t = "myForm"
module EntryValue = FormDataEntryValue
open EntryValue
let logEntry = (~stringPrefix: string, ~filePrefix: string, entry: EntryValue.t) =>
switch entry {
| String(value) => Console.log(`${stringPrefix}${value}`)
| File(file) => Console.log(`${filePrefix}${file.name}`)
}
let formData: FormData.t = FormData.make(~form=myForm)
// Get a form field - returns formDataEntryValue which could be string or WebApiFile
let phoneEntry: null<EntryValue.t> = formData->FormData.get("phone")
// Decode the entry to handle both string and WebApiFile cases
let _ = switch phoneEntry->Null.toOption {
| None => Console.log("No phone field")
| Some(entry) => logEntry(~stringPrefix="Phone: ", ~filePrefix="Unexpected file: ", entry)
}
// Get all values for a field (useful for multi-select or multiple file inputs)
let allImages: array<EntryValue.t> = formData->FormData.getAll("images")
let _ =
allImages->Array.forEach(entry =>
logEntry(~stringPrefix="String value: ", ~filePrefix="WebApiFile: ", entry)
)
// Create formDataEntryValue from string or file
let stringEntry = EntryValue.String("test value")
let blob: Blob.t = Blob.make(~blobParts=[])
let file: File.t = File.make(~fileBits=[], ~fileName="test.txt")
let fileEntry = EntryValue.File(file)
formData->FormData.appendBlob(~name="avatar", ~blobValue=blob)
logEntry(~stringPrefix="String entry: ", ~filePrefix="Unexpected file entry: ", stringEntry)
logEntry(~stringPrefix="Unexpected string entry: ", ~filePrefix="File entry: ", fileEntry)
// Iterate over all entries in the FormData
let entries: Iterator.t<(string, EntryValue.t)> = formData->FormData.entries
let _ = entries->Iterator.forEach(((key, value)) => {
switch value {
| String(s) => Console.log(`${key}: ${s}`)
| File(f) => Console.log(`${key}: [WebApiFile] ${f.name}`)
}
})

Headers__test

let h = Headers.make()
h->Headers.set(~name="X-Test", ~value="1")
let h2 = Headers.fromDict(dict{"X-Vegetable": "Carrot"})
let h3 = Headers.fromKeyValueArray([("X-Fruit", "Apple"), ("X-Vegetable", "Carrot")])
Console.log(h3->Headers.get("X-Fruit"))

HTMLCanvasElement__test

external toHTMLCanvasElement: null<DomTypes.element> => DomTypes.htmlCanvasElement = "%identity"
@set
external setFillStyle: (DomTypes.canvasRenderingContext2D, CanvasTypes.fillStyle) => unit =
"fillStyle"
@get
external getFillStyle: DomTypes.canvasRenderingContext2D => CanvasTypes.fillStyle = "fillStyle"
@set
external setFont: (DomTypes.canvasRenderingContext2D, string) => unit = "font"
@set
external setTextBaseline: (
DomTypes.canvasRenderingContext2D,
CanvasTypes.canvasTextBaseline,
) => unit = "textBaseline"
let myCanvas: DomTypes.htmlCanvasElement =
DomGlobal.document->Document.getElementById("myCanvas")->toHTMLCanvasElement
let ctx = myCanvas->HTMLCanvasElement.getContext2D
ctx->setFillStyle(FillStyle.fromString("red"))
ctx->CanvasRenderingContext2D.fillRect(~x=50., ~y=50., ~w=200., ~h=200.)
ctx->setFillStyle(FillStyle.fromString("black"))
ctx->setFont("2px Tahoma")
ctx->setTextBaseline(CanvasTypes.Top)
ctx->CanvasRenderingContext2D.fillText(~text="MY TEXT", ~x=60., ~y=60.)
switch ctx->getFillStyle->FillStyle.decode {
| FillStyle.String(color) => Console.log(`Color: ${color}`)
| FillStyle.CanvasGradient(_) => Console.log("CanvasGradient")
| FillStyle.CanvasPattern(_) => Console.log("CanvasPattern")
}
let img: DomTypes.htmlImageElement = DomGlobal.document->Document.createElement("img")->Obj.magic
ctx->CanvasRenderingContext2D.drawImageWithDimensions(
~image=img,
~dx=0.,
~dy=0.,
~dw=200.,
~dh=200.,
)

HTMLElement__test

DomGlobal.document
->Document.querySelector("form")
->Null.toOption
->Option.forEach(form => {
form->Element.scrollIntoViewWithOptions({behavior: DomTypes.Smooth})
})

HTMLInputElement__test

external toHTMLInputElement: DomTypes.element => DomTypes.htmlInputElement = "%identity"
let input: DomTypes.htmlInputElement =
DomGlobal.document->Document.createElement("input")->toHTMLInputElement
let value = input.value

ImageData__test

let imageData = ImageData.makeWithData(~data=Uint8ClampedArray.fromArray([]), ~sw=100, ~sh=100)
imageData.data->TypedArray.length->Console.log

IntersectionObserver__test

module IObserver = IntersectionObserver
module IRoot = IntersectionObserverRoot
let observer = IObserver.make(~callback=(entry, observer) => {
Console.log2(entry, observer)
})
let root = DomGlobal.document->Document.querySelector("#root")->Null.getUnsafe
let observer2 = IObserver.make(
~callback=(entry, observer) => {
Console.log2(entry, observer)
},
~options={
root: root->IRoot.fromElement,
rootMargin: "10px",
threshold: [0.1],
},
)
switch observer2.root->IRoot.decode {
| Element(_) => Console.log("Element")
| Document(_) => Console.log("Document")
| Null => Console.log("Null")
}
let rootMargin2 = observer2.rootMargin
let targetElement = DomGlobal.document->Document.querySelector("#targetElement")->Null.toOption
switch targetElement {
| Some(e) => {
observer2->IObserver.observe(e)
observer2->IObserver.unobserve(e)
}
| _ => Console.log("Target element not found.")
}
let entries2 = observer2->IObserver.takeRecords
Console.log(entries2->Array.length)
observer2->IObserver.disconnect

Location__test

let location = DomGlobal.document.location
// Access properties using `.`
let href = location.href
// Invoke methods using the `->TypeModule`
location->Location.reload
let a = 0

MediaStream__test

let mediaStream: MediaCaptureAndStreamsTypes.mediaStream = Obj.magic()
let mediaStreamTrack: MediaCaptureAndStreamsTypes.mediaStreamTrack = Obj.magic()
let _make = MediaStream.make()
let _fromMediaStream = MediaStream.fromMediaStream(mediaStream)
let _fromTracks = MediaStream.fromTracks([mediaStreamTrack])

MutationObserver__test

module MObserver = MutationObserver
let observer = MObserver.make((mutations, obs) => {
let button = DomGlobal.document->Document.querySelector("button")
switch button->Null.toOption {
| Some(button) => {
Console.log(button)
obs->MObserver.disconnect
}
| None => ()
}
Console.log(mutations)
})
observer->MObserver.observe(
~target=DomGlobal.document->Document.asNode,
~options={childList: true, subtree: true},
)
let records = observer->MObserver.takeRecords
Console.log(records->Array.length)
observer->MObserver.disconnect

Notification__test

let current = Notification.permission
Notification.requestPermission()
->Promise.thenResolve(notificationPermission => {
switch notificationPermission {
| Granted => Console.log("Permission granted")
| Denied => Console.log("Permission denied")
| Default => Console.log("Permission default")
}
})
->Promise.ignore

OfflineAudioContext__test

let offlineAudioContextOptions: WebAudioTypes.offlineAudioContextOptions = Obj.magic()
let _fromOptions = OfflineAudioContext.fromOptions(offlineAudioContextOptions)
let _fromChannelCountLengthAndSampleRate = OfflineAudioContext.fromChannelCountLengthAndSampleRate(
~numberOfChannels=2,
~length=1024,
~sampleRate=44_100.,
)

Path2D__test

let path2D: CanvasTypes.path2D = Obj.magic()
let _make = Path2D.make()
let _fromPath2D = Path2D.fromPath2D(path2D)
let _fromString = Path2D.fromString("M0 0 L10 10")

ReadableStream__test

let underlyingSource: FileTypes.underlyingSource<string> = Obj.magic()
let strategy: FileTypes.queuingStrategy<string> = Obj.magic()
let _make: ReadableStream.t<string> = ReadableStream.make()
let _fromUnderlyingSource = ReadableStream.fromUnderlyingSource(underlyingSource)
let _fromUnderlyingSourceWithStrategy = ReadableStream.fromUnderlyingSource(
underlyingSource,
~strategy,
)

Request__test

let req = Request.fromURL("https://example.com")
let blob = Blob.make(~blobParts=[])
let file = File.make(~fileBits=[], ~fileName="hello.txt")
let params = URLSearchParams.fromString("greeting=hello")
let formData = FormData.make()
let stream = ReadableStream.make()
let stringBody = BodyInit.fromString("hello")
let blobBody = BodyInit.fromBlob(blob)
let fileBody = BodyInit.fromFile(file)
let paramsBody = BodyInit.fromURLSearchParams(params)
let formDataBody = BodyInit.fromFormData(formData)
let streamBody = BodyInit.fromReadableStream(stream)
let req1 = Request.fromURL("https://example.com/api", ~init={method: "POST", body: stringBody})
let req2 = Request.fromRequest(req1)
let _blob: Blob.t = await req2->Request.clone->Request.blob
let formDataReq = Request.fromURL(
"https://example.com/form",
~init={method: "POST", body: formDataBody},
)
let _formData: FormData.t = await formDataReq->Request.formData
Console.log(await req2->Request.text)

Response__test

let headers = HeadersInit.fromDict(dict{"X-Fruit": "Peach"})
let blob = Blob.make(~blobParts=[])
let file = File.make(~fileBits=[], ~fileName="pong.txt")
let params = URLSearchParams.fromString("fruit=peach")
let formData = FormData.make()
let stream = ReadableStream.make()
let response = Response.fromNull(~init={status: 204, headers})
let response1 = Response.fromString("pong", ~init={status: 200, headers})
let response2 = Response.fromBlob(blob)
let response3 = Response.fromFile(file)
let response4 = Response.fromURLSearchParams(params)
let response5 = Response.fromFormData(formData)
let response6 = Response.fromReadableStream(stream)
let _blob: Blob.t = await response1
->Response.clone
->Response.blob
let _formData: FormData.t = await response5->Response.formData

ServiceWorker__test

let self = ServiceWorkerScope.current
self->ServiceWorkerScope.addEventListener(EventTypes.Push, (event: PushEvent.t) => {
Console.log("received push event")
// Extract data
let (title, body) = switch event.data {
| Some(data) =>
switch data->PushMessageData.json {
| JSON.Object(dict{"title": JSON.String(title), "body": JSON.String(body)}) => (title, body)
| _ => ("???", "???")
}
| None => ("???", "???")
}
// Handle some data sync
event->PushEvent.waitUntil(self->ServiceWorkerScope.fetch("https://rescript-lang.org"))
// Show notification
self.registration
->ServiceWorkerRegistration.showNotification(
~title,
~options={
body,
icon: "/icon.png",
actions: [{action: "open", title: "Open"}, {action: "close", title: "Close"}],
// For example the id of a new data entry
data: JSON.Number(17.),
vibrate: [200, 50, 200, 50, 400],
},
)
->Promise.ignore
})
self->ServiceWorkerScope.addEventListener(EventTypes.NotificationClick, (
event: Notification.notificationEvent,
) => {
Console.log(`notification clicked: ${event.action}`)
// Close the notification
event.notification->Notification.close
// Open a new window if that is relevant
event.notification.data
->Option.flatMap(data => {
switch data {
| JSON.Number(id) => Some(Float.toString(id))
| _ => None
}
})
->Option.forEach(id => {
self.clients
->Clients.openWindow(`https://mywebsite.com/mydata/${id}`)
->Promise.ignore
})
})

SharedWorker__test

let shared1: SharedWorker.t = SharedWorker.make("sharedworker.js")
let shared2: SharedWorker.t = SharedWorker.makeWithName("sharedworker.js", "name")
let shared3: SharedWorker.t = SharedWorker.makeWithOptions(
"sharedworker.js",
{
name: "workerName",
type_: WebWorkersTypes.Module,
},
)
let port: MessagePort.t = SharedWorker.port(shared1)
let self = SharedWorkerScope.current
self->SharedWorkerScope.close

SharedWorkerScope__test

let self = SharedWorkerScope.current
self->SharedWorkerScope.close

Storage__test

for i in 0 to DomGlobal.localStorage.length - 1 {
DomGlobal.localStorage->Storage.key(i)->Null.getOr("nothing")->Console.log
}
let item1 = DomGlobal.localStorage->Storage.getItem("foo")->Null.getOr("nothing")
DomGlobal.localStorage->Storage.setItem(~key="bar", ~value="...")
DomGlobal.localStorage->Storage.removeItem("bar")
DomGlobal.localStorage->Storage.clear

URL__test

let url = URL.make(~url="/foo", ~base="https://bar.com")

URLSearchParams__test

let params1 = URLSearchParams.fromString("foo=1&bar=2")
params1->URLSearchParams.keys->Iterator.toArray->Array.forEach(Console.log)
let params2 = URLSearchParams.fromKeyValueArray([("foo", "1"), ("bar", "b")])
params2->URLSearchParams.values->Iterator.toArray->Array.forEach(Console.log)
let params3 = URLSearchParams.fromDict(dict{"foo": "1", "bar": "b"})
params3
->URLSearchParams.entries
->Iterator.toArray
->Array.forEach(((key, value)) => Console.log2(key, value))
let paramStr = params3->URLSearchParams.toString

VideoFrame__test

let htmlImageElement: DomTypes.htmlImageElement = Obj.magic()
let svgImageElement: DomTypes.svgImageElement = Obj.magic()
let htmlVideoElement: DomTypes.htmlVideoElement = Obj.magic()
let htmlCanvasElement: DomTypes.htmlCanvasElement = Obj.magic()
let imageBitmap: CanvasTypes.imageBitmap = Obj.magic()
let offscreenCanvas: CanvasTypes.offscreenCanvas = Obj.magic()
let videoFrame: DomTypes.videoFrame = Obj.magic()
let arrayBuffer: ArrayBuffer.t = Obj.magic()
let typedArray: TypedArray.t<int> = Obj.magic()
let dataView: DataView.t = Obj.magic()
let videoFrameInit: DomTypes.videoFrameInit = Obj.magic()
let videoFrameBufferInit: DomTypes.videoFrameBufferInit = Obj.magic()
let _fromHTMLImageElement = VideoFrame.fromHTMLImageElement(
~image=htmlImageElement,
~init=videoFrameInit,
)
let _fromSVGImageElement = VideoFrame.fromSVGImageElement(
~image=svgImageElement,
~init=videoFrameInit,
)
let _fromHTMLVideoElement = VideoFrame.fromHTMLVideoElement(
~image=htmlVideoElement,
~init=videoFrameInit,
)
let _fromHTMLCanvasElement = VideoFrame.fromHTMLCanvasElement(
~image=htmlCanvasElement,
~init=videoFrameInit,
)
let _fromImageBitmap = VideoFrame.fromImageBitmap(~image=imageBitmap, ~init=videoFrameInit)
let _fromOffscreenCanvas = VideoFrame.fromOffscreenCanvas(
~image=offscreenCanvas,
~init=videoFrameInit,
)
let _fromVideoFrame = VideoFrame.fromVideoFrame(~image=videoFrame, ~init=videoFrameInit)
let _fromArrayBuffer = VideoFrame.fromArrayBuffer(~data=arrayBuffer, ~init=videoFrameBufferInit)
let _fromTypedArray = VideoFrame.fromTypedArray(~data=typedArray, ~init=videoFrameBufferInit)
let _fromDataView = VideoFrame.fromDataView(~data=dataView, ~init=videoFrameBufferInit)

VisualViewport__test

let maybeViewport: Null.t<VisualViewport.t> = Window.current->Window.visualViewport

WebSocket__test

let _fromURL = WebSocket.fromURL(~url="wss://example.com/socket")
let _fromURLWithProtocols = WebSocket.fromURLWithProtocols(
~url="wss://example.com/socket",
~protocols=["chat", "superchat"],
)