Skip to content

A ReScript Library for Interactive User Interfaces

Build components and web applications with fine-grained reactivity in a sound type system world.

Fine-grained reactivity

Signals, computeds, and effects recompute only what changed. No virtual DOM diff.

Learn about signals →

Sound type system

ReScript catches invalid states at compile time with exhaustive matching and stronger null-safety by default.

Read the introduction →

Minimal footprint

A small runtime, tree-shakeable modules, and built-in primitives instead of a stack of add-on packages.

Read the overview →

JSX support + built-in router

Write components in JSX and handle routing with first-party primitives instead of stitching the basics together yourself.

Components and router →
01

Build a presentational component

Components are plain functions. TemperatureDisplay takes a value and a unit and renders them in a styled card.

TemperatureDisplay.res
1type tempUnit = Celsius | Fahrenheit | Kelvin
2
3let symbolFor = u =>
4 switch u {
5 | Celsius => "°C"
6 | Fahrenheit => "°F"
7 | Kelvin => "K"
8 }
9
10@jsx.component
11let make = (~value: float, ~unit: tempUnit) =>
12 <div class="temp-display">
13 <span class="temp-value">
14 {Node.text(value->Float.toFixed(~digits=1))}
15 </span>
16 <span class="temp-unit">
17 {Node.text(symbolFor(unit))}
18 </span>
19 </div>
22.0°CCelsius
Preview — TemperatureDisplay with a static value of 22 °C
02

Add reactivity with signals and computeds

One signal holds the Celsius value. Computeds derive Fahrenheit and Kelvin automatically. Move the slider — the display updates without re-rendering the tree.

TemperatureDashboard.res
1let celsius = Signal.make(22.0)
2
3let fahrenheit = Computed.make(() =>
4 Signal.get(celsius) *. 9.0 /. 5.0 +. 32.0
5)
6let kelvin = Computed.make(() =>
7 Signal.get(celsius) +. 273.15
8)
9
10@jsx.component
11let make = () =>
12 <div class="temp-row">
13 <TemperatureDisplay
14 value={() => Signal.get(celsius)} unit=Celsius
15 />
16 <TemperatureDisplay
17 value={() => Signal.get(fahrenheit)} unit=Fahrenheit
18 />
19 <TemperatureDisplay
20 value={() => Signal.get(kelvin)} unit=Kelvin
21 />
22 </div>
22.0°CCelsius
71.6°FFahrenheit
295.1KKelvin
Preview — a single signal drives three synchronized readouts
03

Fetch live data in an effect

An effect tracks the selected capital and fetches the current temperature from the Open-Meteo API. When the capital changes, the effect re-runs and the dashboard re-renders.

CapitalWeather.res
1let capital = Signal.make(pickRandomCapital())
2let celsius = Signal.make(None)
3
4let fahrenheit = Computed.make(() =>
5 switch Signal.get(celsius) {
6 | Some(c) => Some(c *. 9.0 /. 5.0 +. 32.0)
7 | None => None
8 }
9)
10
11Effect.run(() => {
12 let c = Signal.get(capital)
13 Signal.set(celsius, None)
14
15 let url =
16 `https://api.open-meteo.com/v1/forecast?` ++
17 `latitude=${c.lat}&longitude=${c.lng}` ++
18 `&current_weather=true`
19
20 fetch(url)
21 ->Promise.then(r => r->Response.json)
22 ->Promise.then(json => {
23 Signal.set(celsius, Some(json["current_weather"]["temperature"]))
24 Promise.resolve()
25 })
26 ->ignore
27
28 None
29})
Now inBrasília
°CCelsius
°FFahrenheit
KKelvin
Fetching current weather…
Preview — real weather from a random world capital (Open-Meteo)