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 →Build a presentational component
Components are plain functions. TemperatureDisplay takes a value and a unit and renders them in a styled card.
1type tempUnit = Celsius | Fahrenheit | Kelvin23let symbolFor = u =>4 switch u {5 | Celsius => "°C"6 | Fahrenheit => "°F"7 | Kelvin => "K"8 }910@jsx.component11let 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>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.
1let celsius = Signal.make(22.0)23let fahrenheit = Computed.make(() =>4 Signal.get(celsius) *. 9.0 /. 5.0 +. 32.05)6let kelvin = Computed.make(() =>7 Signal.get(celsius) +. 273.158)910@jsx.component11let make = () =>12 <div class="temp-row">13 <TemperatureDisplay14 value={() => Signal.get(celsius)} unit=Celsius15 />16 <TemperatureDisplay17 value={() => Signal.get(fahrenheit)} unit=Fahrenheit18 />19 <TemperatureDisplay20 value={() => Signal.get(kelvin)} unit=Kelvin21 />22 </div>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.
1let capital = Signal.make(pickRandomCapital())2let celsius = Signal.make(None)34let fahrenheit = Computed.make(() =>5 switch Signal.get(celsius) {6 | Some(c) => Some(c *. 9.0 /. 5.0 +. 32.0)7 | None => None8 }9)1011Effect.run(() => {12 let c = Signal.get(capital)13 Signal.set(celsius, None)1415 let url =16 `https://api.open-meteo.com/v1/forecast?` ++17 `latitude=${c.lat}&longitude=${c.lng}` ++18 `¤t_weather=true`1920 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 ->ignore2728 None29})