|
| 1 | +--- |
| 2 | +author: rescript-team |
| 3 | +date: "2025-11-17" |
| 4 | +previewImg: /static/blog/rescript-12-reforging-build-system.webp |
| 5 | +articleImg: /static/blog/rescript-12-reforging-build-system.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 | +## Breaking Changes |
| 22 | + |
| 23 | +### Build system redesign |
| 24 | + |
| 25 | +ReScript 12 ships with the new build system introduced earlier this month in the [Reforging the Build System](./2025-11-04-reforging-build-system.mdx) preview. |
| 26 | +The tooling now relies on a modern architecture that tracks dependencies more precisely, avoids unnecessary recompilations, and integrates with incremental workflows. |
| 27 | +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. |
| 28 | + |
| 29 | +### Runtime package split |
| 30 | + |
| 31 | +The compiler no longer embeds runtime modules. |
| 32 | +Instead, the runtime lives in the dedicated `@rescript/runtime` npm package, which contains Belt, Stdlib, Js, primitive modules, and supporting artifacts compiled in both ES Module and CommonJS formats. |
| 33 | +Earlier versions bundled these files inside the `rescript` package and exposed an optional `@rescript/std` helper. |
| 34 | +ReScript 12 removes `@rescript/std`; install `rescript` and `@rescript/runtime` together to keep projects in sync. |
| 35 | +Projects that previously published custom runtime shims should revisit their setup to ensure the new package structure remains discoverable. |
| 36 | + |
| 37 | +### Updated bitwise operator surface |
| 38 | + |
| 39 | +Legacy OCaml-style bitwise functions such as `land`, `lor`, and `lsl` are deprecated. |
| 40 | +They continue to work in v12 with warnings but will be removed in v13. |
| 41 | +ReScript now prefers unified operator syntax: `&&&`, `|||`, `^^^`, and `~~~` cover AND, OR, XOR, and NOT for both `int` and `bigint`. |
| 42 | + |
| 43 | +### API naming alignment |
| 44 | + |
| 45 | +APIs that previously ended with `Exn` now end with `OrThrow`. |
| 46 | +Examples include `Option.getOrThrow`, `List.headOrThrow`, `BigInt.fromStringOrThrow`, and `JSON.parseOrThrow`. |
| 47 | +The change clarifies that these functions throw JavaScript errors, aligning the naming with the language’s semantics. |
| 48 | +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. |
| 49 | +Be aware that `Result.getOrThrow` now throws a JavaScript `Error`, so update any exception handling logic that depended on OCaml exception names. |
| 50 | + |
| 51 | +### JSX version requirement |
| 52 | + |
| 53 | +JSX v3 has been removed. |
| 54 | +ReScript 12 requires JSX v4 configuration in `rescript.json`, using the `"jsx": { "version": 4 }` schema. |
| 55 | +ReScript React projects must update their configuration before moving to v12. |
| 56 | +Projects attempting to compile with v3 will receive an explicit error, ensuring that your codebase uses the current transform and associated tooling. |
| 57 | + |
| 58 | +### Deprecation of OCaml compatibility helpers |
| 59 | + |
| 60 | +The standard library continues its shift away from OCaml-specific aliases. |
| 61 | +Functions such as `succ`, `pred`, `abs_float`, `string_of_int`, `fst`, `raise`, and the `char` type are now deprecated. |
| 62 | +The recommended replacements are the JavaScript-aligned counterparts in `Int`, `Float`, `Bool`, `Pair`, and related modules, alongside the `throw` keyword for exceptions. |
| 63 | +References to the OCaml composition operators (`|>`, `@@`) now warn and will be removed in v13; the ReScript pipe operator `->` replaces them. |
| 64 | +The migration tool highlights deprecated usage, and incremental cleanups are encouraged so your codebase is ready before the next major release. |
| 65 | + |
| 66 | +## New features |
| 67 | + |
| 68 | +### Unified operators |
| 69 | + |
| 70 | +ReScript 12 finalizes the unified operator work introduced [earlier this year](./2025-04-11-introducing-unified-operators.mdx). |
| 71 | +Arithmetic, comparison, and bitwise operators now behave consistently across `int` and `bigint`, allowing the compiler to infer the correct specialization from the left operand. |
| 72 | + |
| 73 | +### Expanded bitwise capabilities |
| 74 | + |
| 75 | +In addition to deprecating the OCaml-style helpers, ReScript 12 adds new operator spellings. |
| 76 | +JavaScript-style bitwise operators (`&`, `|`, `^`, `~`) and shift operators (`<<`, `>>`, `>>>`) are first-class citizens that compile directly to their JavaScript equivalents. |
| 77 | +Combined with the unified F#-style operators, developers can select the syntax that best fits their code policies without sacrificing performance or type safety. |
| 78 | + |
| 79 | +### Dict literals and pattern matching |
| 80 | + |
| 81 | +The language now supports dictionary literals (`dict{"foo": "bar"}`) that compile to plain JavaScript objects. |
| 82 | +Dict literals work with variables, multi-line formatting, and optional entries, and they drastically reduce the boilerplate compared to `Dict.fromArray`. |
| 83 | +Pattern matching also understands dicts, enabling concise destructuring of JSON payloads and configuration objects. |
| 84 | +The compiler emits the same optimized JavaScript while preserving ReScript's type guarantees. |
| 85 | + |
| 86 | +```rescript |
| 87 | +let user = dict{"name": "Ada", "role": "engineer"} |
| 88 | +
|
| 89 | +switch user { |
| 90 | +| dict{"name": name, "role": role} => Console.log2(name, role) |
| 91 | +| _ => Console.log("missing user metadata") |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +### Nested and inline record types |
| 96 | + |
| 97 | +Nested record definitions and inline record payloads remove the need for auxiliary type declarations. |
| 98 | +You can define optional nested structures directly inside records, or attach record payloads to variant constructors without creating standalone types. |
| 99 | +The feature supports mutable fields, type parameters, and record spreading, providing better locality for complex domain models with no runtime penalty. |
| 100 | + |
| 101 | +```rescript |
| 102 | +type profile = { |
| 103 | + name: string, |
| 104 | + extra?: { |
| 105 | + location: {city: string, country: string}, |
| 106 | + mutable note: option<string>, |
| 107 | + }, |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +### Variant pattern spreads |
| 112 | + |
| 113 | +Pattern spreads (`| ...SomeVariant as value =>`) allow you to reuse handlers for entire subsets of constructors. |
| 114 | +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. |
| 115 | +The compiler enforces subtype relationships and ensures that runtime representations remain compatible. |
| 116 | + |
| 117 | +```rescript |
| 118 | +type base = Start | Stop | Pause |
| 119 | +type extended = | ...base | Resume |
| 120 | +
|
| 121 | +let handle = (event: extended) => |
| 122 | + switch event { |
| 123 | + | ...base as core => Console.log2("base", core) |
| 124 | + | Resume => Console.log("resuming") |
| 125 | + } |
| 126 | +``` |
| 127 | + |
| 128 | +### JSX preserve mode |
| 129 | + |
| 130 | +Projects that rely on downstream JSX tooling can enable [preserve mode](../docs/manual/v12.0.0/jsx#preserve-mode) via `"jsx": { "version": 4, "preserve": true }`. |
| 131 | +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. |
| 132 | +This mode keeps JSX readable in the output, retains spread props, and maintains compatibility with React Server Components. |
| 133 | +React classic mode is no longer supported, so projects must use the JSX v4 transform regardless of preserve mode settings. |
| 134 | +When preserve mode is disabled, the compiler continues to output the optimized runtime calls you are accustomed to. |
| 135 | + |
| 136 | +### Function-level directives |
| 137 | + |
| 138 | +The new `@directive` attribute emits JavaScript directive strings at the top of generated functions. |
| 139 | +Use it to mark server actions with `'use server'`, memoized handlers with `'use memo'`, or any other directive that your framework requires. |
| 140 | +The attribute works on synchronous and asynchronous functions, supports labeled parameters, and removes the need for `%raw` blocks. |
| 141 | +Combined with JSX preserve mode, this enables clean integration with [React Server Components](../docs/react/latest/server-components.mdx) and other directive-based runtimes. |
| 142 | + |
| 143 | +### Regex literals |
| 144 | + |
| 145 | +ReScript now understands JavaScript-style regular expression literals (`/pattern/flags`). |
| 146 | +They are full equivalents of `%re` expressions, supporting all ECMAScript flags, Unicode character classes, and sticky searches. |
| 147 | +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. |
| 148 | + |
| 149 | +```rescript |
| 150 | +let emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/i |
| 151 | +
|
| 152 | +if emailPattern->RegExp.test("contact@rescript-lang.org") { |
| 153 | + Console.log("Valid email") |
| 154 | +} |
| 155 | +
|
| 156 | +switch emailPattern->RegExp.exec("invalid") { |
| 157 | +| Some(match) => Console.log(match->RegExp.Result.fullMatch) |
| 158 | +| None => Console.log("No match") |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +### Experimental `let?` syntax |
| 163 | + |
| 164 | +This release introduces an experimental `let?` syntax for zero-cost unwrapping of `option` and `result` values. |
| 165 | +The syntax remains behind an opt-in flag while the community evaluates its ergonomics. |
| 166 | +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. |
| 167 | + |
| 168 | +```rescript |
| 169 | +type user = { |
| 170 | + address?: { |
| 171 | + city?: string, |
| 172 | + }, |
| 173 | +} |
| 174 | +
|
| 175 | +let userCity = (user: user): option<string> => { |
| 176 | + let? Some(address) = user.address |
| 177 | + let? Some(city) = address.city |
| 178 | + Some(city) |
| 179 | +} |
| 180 | +``` |
| 181 | + |
| 182 | +## Platform and tooling improvements |
| 183 | + |
| 184 | +### Cleaner JavaScript output |
| 185 | + |
| 186 | +The printer now emits compact arrow functions and streamlines anonymous function expressions, making generated JavaScript easier to audit. |
| 187 | +These changes preserve semantics while aligning the output with modern JavaScript style. |
| 188 | + |
| 189 | +### Internal architecture updates |
| 190 | + |
| 191 | +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. |
| 192 | +These changes simplify future feature work and reduce maintenance overhead. |
| 193 | + |
| 194 | +### ESM-first distribution |
| 195 | + |
| 196 | +The `rescript` npm package declares `"type": "module"` and ships ESM code across the CLI. |
| 197 | +Import statements replace CommonJS `require` usage, improving compatibility with contemporary bundlers and enabling better tree-shaking. |
| 198 | + |
| 199 | +### OCaml 5.3 toolchain |
| 200 | + |
| 201 | +The compiler and supporting tooling now build on OCaml 5.3.0. |
| 202 | +Contributors compiling ReScript from source will need the updated toolchain, and the upgrade brings performance and language runtime improvements from the upstream project. |
| 203 | + |
| 204 | +### Platform-specific binaries |
| 205 | + |
| 206 | +Installer footprints shrink thanks to platform-specific binary packages such as `@rescript/darwin-arm64`, `@rescript/linux-x64`, and `@rescript/win32-x64`. |
| 207 | +npm installs only the binary that matches your operating system and architecture, delivering substantially faster installs and smaller cache footprints for CI pipelines. |
| 208 | +The primary `rescript` package loads the appropriate binary at runtime and surfaces clear error messages if the matching package is missing. |
| 209 | + |
| 210 | +## Acknowledgments |
| 211 | + |
| 212 | + |
| 213 | + |
| 214 | +Thank you to every contributor, tester, and partner who helped shape ReScript 12. |
| 215 | +The core team gathered in Vienna earlier this year to map out this release, and your feedback guided every decision. |
| 216 | +Community pull requests, bug reports, and experiments across the ecosystem gave us the confidence to complete large refactors and deprecations. |
| 217 | + |
| 218 | +## Reach out |
| 219 | + |
| 220 | +Join the conversation on the community forum if you have migration questions or want to share what you build with ReScript 12. |
| 221 | +Businesses that rely on ReScript can contact the association at https://rescript-association.org/contact to explore support, sponsorship, or collaboration. |
| 222 | +Funding enables the team to ship features like the new runtime architecture faster, so every contribution, financial or otherwise, helps the language move forward. |
| 223 | + |
| 224 | +We look forward to hearing what you create with ReScript 12. |
| 225 | + |
| 226 | + |
0 commit comments