Skip to content

Commit 9cea1f3

Browse files
fhammerschmidtnojaftsnobip
authored
V12 post v2 (#1144)
* Rough outline of general v12 blog post * Remove ocaml toolchain * V12 article image * Don't use v12 pic as article image * Rename release blogpost file * Link to migration guide * Remove accidentally committed changelog * Fix links and images * Set date to the 25th * Add v12 release banner * Better quality retreat photo * Update markdown-pages/blog/release-12-0-0.mdx Co-authored-by: Paul Tsnobiladzé <paul.tsnobiladze@gmail.com> * Update markdown-pages/blog/release-12-0-0.mdx Co-authored-by: Paul Tsnobiladzé <paul.tsnobiladze@gmail.com> * Fix previewImg url * Format * Let's not mention v13 yet * Better article img * Fine-tuning * Format --------- Co-authored-by: nojaf <florian.verdonck@outlook.com> Co-authored-by: Paul Tsnobiladzé <paul.tsnobiladze@gmail.com>
1 parent d76901a commit 9cea1f3

File tree

7 files changed

+273
-0
lines changed

7 files changed

+273
-0
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
---
2+
author: rescript-team
3+
date: "2025-11-25"
4+
previewImg: /blog/compiler_release_12_0.webp
5+
articleImg: /blog/compiler_release_12_0_article.webp
6+
badge: release
7+
title: "Announcing ReScript 12"
8+
description: |
9+
ReScript 12 arrives with a redesigned build toolchain, a modular runtime, and a wave of ergonomic language features.
10+
---
11+
12+
## Introduction
13+
14+
ReScript 12 is now available.
15+
This release completes the multi-year effort to separate the compiler from legacy constraints, refreshes critical developer workflows, and delivers a modern feature set that is grounded in real-world feedback.
16+
The headline upgrades include a rewritten build system, a standalone runtime package, unified operator support, JSX preserve mode, and numerous syntax improvements that make ReScript code easier to write and maintain.
17+
18+
Teams upgrading from ReScript 11 should review the highlighted breaking changes and schedule time for migration.
19+
The new tooling provides focused commands, better diagnostics, and smaller downloads, while the language additions flatten sharp edges that many of you encountered in larger codebases.
20+
21+
You can find the complete migration guide [here](../docs/manual/migrate-to-v12.mdx).
22+
23+
## New Features
24+
25+
### New Build System
26+
27+
ReScript 12 ships with the new build system introduced earlier this month in the [Reforging the Build System](./reforging-build-system.mdx) preview.
28+
The tooling now relies on a modern architecture that tracks dependencies more precisely, avoids unnecessary recompilations, and integrates with incremental workflows even across monorepo packages.
29+
30+
### Improved Standard Library
31+
32+
The new standard library [`@rescript/core`](https://github.com/rescript-lang/rescript-core) is now included in the compiler runtime. So you can get rid if the dependency on `@rescript/core` in your projects. To avoid collisions we have named the new internal library just `Stdlib`, so you can keep using it alongside `Core` if you cannot migrate yet. There have been tons of additions as well, so you can shrink your `Utils` files down a bit. Check it out here: [Stdlib](../docs/manual/api/stdlib).
33+
34+
### Operator Improvements
35+
36+
#### Unified Operators
37+
38+
ReScript 12 finalizes the unified operator work introduced [earlier this year](./introducing-unified-operators.mdx).
39+
Arithmetic operators (`+`, `-`, `*`, `/`, and the newly added `%` and `**`) now work consistently for `int`, `float` and `bigint`, allowing the compiler to infer the correct specialization from the left operand. You can ditch all the `+.`, `-.`, `*.`, and `/.` now!
40+
41+
In addition you can also now use `+` for string concatenation, while `++` still works as before.
42+
43+
#### Bitwise Operators
44+
45+
ReScript now supports F#-style bitwise operators `&&&` (AND), `|||` (OR), `^^^` (XOR), and `~~~` (NOT) for both `int` and `bigint`.
46+
Legacy OCaml-style bitwise functions such as `land`, `lor`, and `lsl` are deprecated.
47+
48+
#### Shift Operators
49+
50+
For shift operators there was luckily no conflict in the character space, which means ReScript now supports `<<` (logical left shift), `>>` (logical right shift) and `>>>` (unsigned right shift), just like JavaScript.
51+
52+
### Dict Literals and Pattern Matching
53+
54+
The language now supports dictionary literals (`dict{"foo": "bar"}`) that compile to plain JavaScript objects.
55+
Dict literals work with variables, multi-line formatting, and optional entries, and they drastically reduce the boilerplate compared to `Dict.fromArray`.
56+
Pattern matching also understands dicts, enabling concise destructuring of JSON payloads and configuration objects.
57+
The compiler emits the same optimized JavaScript while preserving ReScript's type guarantees.
58+
59+
```rescript
60+
let user = dict{"name": "Ada", "role": "engineer"}
61+
62+
switch user {
63+
| dict{"name": name, "role": role} => Console.log2(name, role)
64+
| _ => Console.log("missing user metadata")
65+
}
66+
```
67+
68+
### Nested Record Types
69+
70+
Nested record definitions remove the need for auxiliary type declarations.
71+
You can define optional nested structures directly inside records, or attach record payloads to variant constructors without creating standalone types.
72+
The feature supports mutable fields, type parameters, and record spreading, providing better locality for complex domain models with no runtime penalty.
73+
74+
```rescript
75+
type profile = {
76+
name: string,
77+
extra?: {
78+
location: {city: string, country: string},
79+
mutable note: option<string>,
80+
},
81+
}
82+
```
83+
84+
### Variant Pattern Spreads
85+
86+
Pattern spreads (`| ...SomeVariant as value =>`) allow you to reuse handlers for entire subsets of constructors.
87+
When a variant extends another variant through spreads, you can match the shared constructors in one branch and delegate to helper functions, keeping exhaustive matches concise even as the hierarchy grows.
88+
The compiler enforces subtype relationships and ensures that runtime representations remain compatible.
89+
90+
```rescript
91+
type base = Start | Stop | Pause
92+
type extended = | ...base | Resume
93+
94+
let handle = (event: extended) =>
95+
switch event {
96+
| ...base as core => Console.log2("base", core)
97+
| Resume => Console.log("resuming")
98+
}
99+
```
100+
101+
### JSX Preserve Mode
102+
103+
Projects that rely on downstream JSX tooling can enable [preserve mode](../docs/manual/jsx.mdx#preserve-mode) via `"jsx": { "version": 4, "preserve": true }`.
104+
The compiler will emit JSX syntax directly instead of transforming elements to `react/jsx-runtime` calls, allowing bundlers such as ESBuild, SWC, or Babel to apply their own transforms.
105+
This mode keeps JSX readable in the output and maintains compatibility with React Compiler.
106+
107+
React classic mode is no longer supported, so projects must use the JSX v4 transform regardless of preserve mode settings.
108+
When preserve mode is disabled, the compiler continues to output the optimized runtime calls you are accustomed to.
109+
110+
### Function-Level Directives
111+
112+
The new `@directive` attribute emits JavaScript directive strings at the top of generated functions.
113+
Use it to mark server actions with `'use server'`, memoized handlers with `'use memo'`, or any other directive that your framework requires.
114+
The attribute works on synchronous and asynchronous functions, supports labeled parameters, and removes the need for `%raw` blocks.
115+
Combined with JSX preserve mode, this enables clean integration with [React Server Components](../docs/react/server-components.mdx) and other directive-based runtimes.
116+
117+
### Regex Literals
118+
119+
ReScript now understands JavaScript-style regular expression literals (`/pattern/flags`).
120+
They are full equivalents of `%re` expressions, supporting all ECMAScript flags, Unicode character classes, and sticky searches.
121+
The literals compile directly to JavaScript regex objects, so existing APIs like `RegExp.exec` and `RegExp.test` continue to work exactly as before, but with clearer syntax and better editor support.
122+
123+
```rescript
124+
let emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i
125+
126+
if emailPattern->RegExp.test("contact@rescript-lang.org") {
127+
Console.log("Valid email")
128+
}
129+
130+
switch emailPattern->RegExp.exec("invalid") {
131+
| Some(match) => Console.log(match->RegExp.Result.fullMatch)
132+
| None => Console.log("No match")
133+
}
134+
```
135+
136+
### Experimental `let?` Syntax
137+
138+
This release introduces an experimental `let?` syntax for zero-cost unwrapping of `option` and `result` values.
139+
The syntax remains behind an opt-in flag while the community evaluates its ergonomics.
140+
Refer to the forum discussion at https://forum.rescript-lang.org/t/proposing-new-syntax-for-zero-cost-unwrapping-options-results/6227 for the current proposal, examples, and feedback guidelines.
141+
142+
```rescript
143+
type user = {
144+
address?: {
145+
city?: string,
146+
},
147+
}
148+
149+
let userCity = (user: user): option<string> => {
150+
let? Some(address) = user.address
151+
let? Some(city) = address.city
152+
Some(city)
153+
}
154+
```
155+
156+
## Platform and Tooling Improvements
157+
158+
### Cleaner JavaScript Output
159+
160+
The printer now emits compact arrow functions and streamlines anonymous function expressions, making generated JavaScript easier to audit.
161+
These changes preserve semantics while aligning the output with modern JavaScript style.
162+
163+
### Internal Architecture Updates
164+
165+
The compiler cleans up its internal abstract syntax tree, removes unused OCaml-era nodes, and tracks async and partial function metadata directly on AST nodes.
166+
These changes simplify future feature work and reduce maintenance overhead.
167+
168+
### ESM-First Distribution
169+
170+
The `rescript` npm package declares `"type": "module"` and ships ESM code across the CLI.
171+
Import statements replace CommonJS `require` usage, improving compatibility with contemporary bundlers and enabling better tree-shaking.
172+
173+
### Platform-Specific Binaries
174+
175+
Installer footprints shrink thanks to platform-specific binary packages such as `@rescript/darwin-arm64`, `@rescript/linux-x64`, and `@rescript/win32-x64`.
176+
npm installs only the binary that matches your operating system and architecture, delivering substantially faster installs and smaller cache footprints for CI pipelines.
177+
The primary `rescript` package loads the appropriate binary at runtime and surfaces clear error messages if the matching package is missing.
178+
179+
## Breaking Changes
180+
181+
### Build System
182+
183+
The old build system remains available through `rescript-legacy`, but active projects should switch to the new commands to benefit from faster feedback loops and clearer output.
184+
185+
### Runtime Package Split
186+
187+
The runtime modules were moved from the main `rescript` npm package to a dedicated `@rescript/runtime` npm package. It is automatically installed as a dependency of the main `rescript` package.
188+
The new `@rescript/runtime` package also replaces the standalone runtime package `@rescript/std` from earlier versions.
189+
190+
### API Naming Alignment
191+
192+
APIs that previously ended with `Exn` now end with `OrThrow`.
193+
Examples include `Option.getOrThrow`, `List.headOrThrow`, `BigInt.fromStringOrThrow`, and `JSON.parseOrThrow`.
194+
The change clarifies that these functions throw JavaScript errors, aligning the naming with the language’s semantics.
195+
The deprecated `*Exn` variants remain available in v12 to ease the transition, and the codemod bundled with the migration tool can perform a mechanical rename.
196+
Note that `Result.getOrThrow` now throws a JavaScript exception, so please update any exception-handling logic that relied on OCaml exception names. We also revamped the API around JS exceptions and errors.
197+
198+
We’ve renamed JS errors to `JsError.t` to better distinguish them from the `Result.Error` variant. Since JavaScript allows throwing anything (not only proper Error objects), the previous way of catching JS exceptions was unsafe:
199+
200+
```res
201+
let foo = switch myUnsafeJsResult() {
202+
| exception Exn.Error(e) => Error(e->Error.message)
203+
// ☝️ this will crash if `e` is undefined or null
204+
| myRes => Ok(myRes)
205+
}
206+
```
207+
208+
Additionally, the coexistence of `Result.Error` and the `Error` module caused confusion.
209+
210+
The new recommended pattern is:
211+
212+
```res
213+
let foo = switch myUnsafeJsResult() {
214+
| exception JsExn(e) => Error(e->JsExn.message))
215+
// ☝️ this is now safe even `e` is undefined or null
216+
| myRes => Ok(myRes)
217+
}
218+
```
219+
220+
Utility helpers for working with JS errors are now in `JsError`, eg:
221+
222+
```res
223+
JsError.throw(JsError.RangeError.make("the payload should be below 16"))
224+
```
225+
226+
### JSX Version Requirement
227+
228+
JSX v3 has been removed.
229+
ReScript 12 requires JSX v4 configuration in `rescript.json`, using the `"jsx": { "version": 4 }` schema.
230+
ReScript React projects must update their configuration before moving to v12.
231+
Projects attempting to compile with v3 will receive an explicit error, ensuring that your codebase uses the current transform and associated tooling.
232+
233+
### Deprecation of OCaml Compatibility Helpers
234+
235+
The standard library continues its shift away from OCaml-specific aliases.
236+
Functions such as `succ`, `pred`, `abs_float`, `string_of_int`, `fst`, `raise`, and the `char` type are now deprecated.
237+
The recommended replacements are the JavaScript-aligned counterparts in `Int`, `Float`, `Bool`, `Pair`, and related modules, alongside the `throw` keyword for exceptions.
238+
References to the OCaml composition operators (`|>`, `@@`) now warn and will be removed in a future version; the ReScript pipe operator `->` replaces them.
239+
The migration tool highlights deprecated usage, and incremental cleanups are encouraged so your codebase is ready before the next major release.
240+
241+
## Acknowledgments
242+
243+
<Image
244+
src="/blog/rescript-retreat-2025.webp"
245+
withShadow={true}
246+
caption="Kick-off of this year's ReScript Retreat in Vienna"
247+
/>
248+
249+
Thank you to every contributor, tester, and partner who helped shape ReScript 12.
250+
The core team gathered in Vienna earlier this year to map out this release, and your feedback guided every decision.
251+
Community pull requests, bug reports, and experiments across the ecosystem gave us the confidence to complete large refactors and deprecations.
252+
253+
## Reach Out
254+
255+
Join the conversation on the community forum if you have migration questions or want to share what you build with ReScript 12.
256+
Businesses that rely on ReScript can contact the association at https://rescript-association.org/contact to explore support, sponsorship, or collaboration.
257+
Funding enables the team to ship features like the new runtime architecture faster, so every contribution, financial or otherwise, helps the language move forward.
258+
259+
We look forward to hearing what you create with ReScript 12.
260+
261+
<Image
262+
src="/blog/rescript-team-2025.webp"
263+
withShadow={true}
264+
caption="The ReScript team at the 2025 retreat in Vienna"
265+
/>
31.3 KB
Loading
33.7 KB
Loading
612 KB
Loading
424 KB
Loading

src/Path.res

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ type t = [
155155
| #"/blog/release-8-4"
156156
| #"/blog/release-8-3"
157157
| #"/blog/release-8-3-2"
158+
| #"/blog/release-12-0-0"
158159
| #"/blog/release-11-1-0"
159160
| #"/blog/release-11-0-0"
160161
| #"/blog/release-10-1"

src/layouts/LandingPageLayout.res

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,13 @@ let make = (~components=MarkdownComponents.default, ~children) => {
688688
<div className="mt-4 xs:mt-16">
689689
<div className="text-gray-80 text-18 z">
690690
<div className="absolute w-full top-16">
691+
<Banner>
692+
{React.string("ReScript 12 is out! Read the ")}
693+
<ReactRouter.Link to=#"/blog/release-12-0-0" className="underline">
694+
{React.string("announcement blog post")}
695+
</ReactRouter.Link>
696+
{React.string(".")}
697+
</Banner>
691698
<div className="relative overflow-hidden pb-32">
692699
<main className="mt-10 min-w-320 lg:align-center w-full">
693700
<div className="">

0 commit comments

Comments
 (0)