+
+```res
+// Set the jsx configuration per module
+@@jsxConfig({version: 4, mode: "automatic"})
+
+module AutomaticModeExample = {
+ // "automatic" mode will compile jsx to the React 18 compatible
+ // jsx-runtime calls
+ @@jsxConfig({version: 4, mode: "automatic"})
+
+ @react.component
+ let make = (~name) => {
+ {React.string(`Hello ${name}`)}
+ }
+}
+
+module ClassicModeExample = {
+ // "classic" mode will compile jsx to React.createElement calls
+ @@jsxConfig({version: 4, mode: "classic"})
+
+ @react.component
+ let make = (~name) => {
+ {React.string(`Hello ${name}`)}
+ }
+}
+
+module NoAttributeExample = {
+ // No need for `makeProps` anymore
+ type props = {name: string}
+
+ let make = (props: props) => {
+ {React.string(`Hello ${props.name}`)}
+ }
+}
+
+module ReactInterfaceExample: {
+ @react.component
+ let make: (~name: string, ~age: int=?) => React.element
+} = {
+ @react.component
+ let make = (~name, ~age=0) => {
+
+ {React.string(
+ `Hello ${name}, you are ${Belt.Int.toString(age)} years old.`,
+ )}
+
+ }
+}
+
+module PropTypeInjectionExample = {
+ // Let's assume we have a prop type that we wanna enforce
+ // as our labeled arguments
+ type someoneElsesProps = {isHuman: bool}
+
+ // Here we tell the `react.component` decorator what props to infer.
+ // Useful for e.g. NextJS usage, or to create components that should
+ // comply to certain library component interfaces
+ @react.component(: someoneElsesProps)
+ let make = (~isHuman) => {
+ let msg = switch isHuman {
+ | true => "hello human"
+ | false => "hello fellow computer"
+ }
+ {React.string(msg)}
+ }
+}
+
+module PropSpreadExample = {
+ // Note: This will require @rescript/react 0.11 or later
+ @@jsxConfig({version: 4, mode: "automatic"})
+
+ @react.component
+ let make = () => {
+ let props = {NoAttributeExample.name: "World"}
+
+
+ }
+}
+
+let root =
+
+```
+
+```js
+import * as React from "react";
+import * as JsxRuntime from "react/jsx-runtime";
+
+function Playground$AutomaticModeExample(props) {
+ return JsxRuntime.jsx("div", {
+ children: "Hello " + props.name + ""
+ });
+}
+
+var AutomaticModeExample = {
+ make: Playground$AutomaticModeExample
+};
+
+function Playground$ClassicModeExample(props) {
+ return React.createElement("div", undefined, "Hello " + props.name + "");
+}
+
+var ClassicModeExample = {
+ make: Playground$ClassicModeExample
+};
+
+function make(props) {
+ return JsxRuntime.jsx("div", {
+ children: "Hello " + props.name + ""
+ });
+}
+
+var NoAttributeExample = {
+ make: make
+};
+
+function Playground$ReactInterfaceExample(props) {
+ var age = props.age;
+ var age$1 = age !== undefined ? age : 0;
+ return JsxRuntime.jsx("div", {
+ children: "Hello " + props.name + ", you are " + String(age$1) + " years old."
+ });
+}
+
+var ReactInterfaceExample = {
+ make: Playground$ReactInterfaceExample
+};
+
+function Playground$PropTypeInjectionExample(props) {
+ var msg = props.isHuman ? "hello human" : "hello fellow computer";
+ return JsxRuntime.jsx("div", {
+ children: msg
+ });
+}
+
+var PropTypeInjectionExample = {
+ make: Playground$PropTypeInjectionExample
+};
+
+function Playground$PropSpreadExample(props) {
+ return JsxRuntime.jsx(make, {
+ name: "World"
+ });
+}
+
+var PropSpreadExample = {
+ make: Playground$PropSpreadExample
+};
+
+var root = JsxRuntime.jsxs("div", {
+ children: [
+ JsxRuntime.jsx(Playground$AutomaticModeExample, {
+ name: "Automatic"
+ }),
+ JsxRuntime.jsx(Playground$ClassicModeExample, {
+ name: "Classic"
+ }),
+ JsxRuntime.jsx(make, {
+ name: "NoAttribute"
+ }),
+ JsxRuntime.jsx(Playground$ReactInterfaceExample, {
+ name: "Interface"
+ }),
+ JsxRuntime.jsx(Playground$PropTypeInjectionExample, {
+ isHuman: true
+ }),
+ JsxRuntime.jsx(Playground$PropSpreadExample, {})
+ ]
+ });
+
+export {
+ AutomaticModeExample ,
+ ClassicModeExample ,
+ NoAttributeExample ,
+ ReactInterfaceExample ,
+ PropTypeInjectionExample ,
+ PropSpreadExample ,
+ root ,
+}
+```
+
+
+### How to migrate to JSX v4?
+
+We provide a full [migration guide](/docs/react/latest/migrate-react) with all the details of an migration.
+
+Make sure to also check out the [rescript-react changelog](https://github.com/rescript-lang/rescript-react/blob/master/CHANGELOG.md) as well.
+
+## What's next?
+
+Our contributors are already one step ahead and are currently working on improvements for the next major v11 release. Things that are currently being explored:
+
+- Make uncurried functions the default. This will be a huge change in terms of how we do interop and will open completely new ways to interact with existing codebases. It will also allow us to improve tooling in ways that wouldn't have been possible in a curried language.
+- Explorations for a community "standard library" that goes beyond `Belt` and `Js.*`. This will also involve disabling / removing global "Stdlib" modules that shouldn't be used (e.g. `Array`, `List`, etc).
+- New tooling to generate markdown from docstrings (module, type and value level). This will be super simple, but very effective.
+- Explorations for a [localized documentation page](https://forum.rescript-lang.org/t/translation-project-rescript-lang-org/4022) (currently in a slowed-down exploration phase, but we will be getting there)
+
+Check out the [v11](https://github.com/rescript-lang/rescript/issues?q=is%3Aopen+is%3Aissue+milestone%3Av11.0) milestone on our `rescript-lang` repo for more details on future improvements.
+
+## Acknowledgements
+
+As always, we want to thank our [contributors](https://github.com/rescript-lang/rescript/graphs/contributors?from=2019-11-24&to=2023-02-02&type=c) for building an amazing platform. Special thanks go out to [mununki](https://github.com/mununki) for building the new JSX v4 syntax. Amazing work!
+
+## That's it
+
+We hope you enjoy the newest improvements as much as we do.
+
+In case there's any issues / problems, make sure to report bugs to [rescript-lang/rescript](https://github.com/rescript-lang/rescript) (language / syntax / jsx), [rescript-lang/rescript-react](https://github.com/rescript-lang/rescript-react) (React 16 / 18 binding) or [rescript-association/rescript-lang.org](https://github.com/rescript-lang/rescript-lang.org) (documentation) repositories.
+
+Also feel free to visit the [ReScript forum](https://forum.rescript-lang.org/) to ask questions and connect with other ReScripters.
diff --git a/_blogposts/2023-04-17-improving-interop.mdx b/_blogposts/2023-04-17-improving-interop.mdx
new file mode 100644
index 000000000..4d883dd96
--- /dev/null
+++ b/_blogposts/2023-04-17-improving-interop.mdx
@@ -0,0 +1,319 @@
+---
+author: rescript-team
+date: "2023-04-17"
+title: Better interop with customizable variants
+badge: roadmap
+description: |
+ A tour of new capabilities coming in ReScript v11
+---
+
+ReScript v11 is around the corner, and it comes packed with new features that will improve interop with JavaScript/TypeScript. Recently we've made some changes to the runtime representation of variants that'll allow you to use variants for a large number of new interop scenarios, zero cost. This is important, because variants are _the_ feature of ReScript, enabling great data modeling, pattern matching and more.
+
+- **Customizable runtime representation.** We're making the runtime representation of variants customizable. This will allow you to cleanly map variants to external data and APIs in many more cases than before.
+- **Zero cost bindings to discriminated unions.** Variants with inline records will map cleanly to JavaScript/TypeScript [discriminated unions](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions).
+- **Unboxed (untagged) variants.** We also introduce untagged variants - variants where the underlying runtime representation can be a primitive, without a specific discriminator. This will let you cleanly map to things like heterogenous array items, nullable values, and more.
+
+Let's dive into the details.
+
+## Tagged variants
+
+Variants with payloads have always been represented as a tagged union at runtime. Here's an example:
+
+```rescript
+type entity = User({name: string}) | Group({workingName: string})
+
+let user = User({name: "Hello"})
+```
+
+This is represented as:
+
+```javascript
+var user = {
+ TAG: /* User */ 0,
+ name: "Hello",
+};
+```
+
+However, this has been problematic when binding to external data because there has been no way to customize the discriminator (the `TAG` property) or how its value is represented for each variant case (`0` representing `User` here). This means that unless your external data is modeled the exact same way as above, which is unlikely, you'd be forced to convert to the structure ReScript expects at runtime.
+
+To illustrate this, let's imagine we're binding to an external union that looks like this in TypeScript:
+
+```typescript
+type LoadingState =
+ | { state: "loading"; ready: boolean }
+ | { state: "error"; message: string }
+ | { state: "done"; data: Data };
+```
+
+Currently, there's no good way to use a ReScript variant to represent this type without resorting to manual and error-prone runtime conversion. However, with the new functionality, binding to the above with no additional runtime cost is easy:
+
+```rescript
+@tag("state")
+type loadingState = | @as("loading") Loading({ready: bool}) | @as("error") Error({message: string}) | @as("done") Done({data: data})
+
+let state = Error({message: "Something went wrong!"})
+```
+
+This will compile to:
+
+```javascript
+var state = {
+ state: "error",
+ message: "Something went wrong!",
+};
+```
+
+Let's break down what we've done to make this work:
+
+- The `@tag` attribute lets you customize the discriminator (default: `TAG`). We're setting that to `"state"` so we map to what the external data looks like.
+- Each variant case has an `@as` attribute. That controls what each variant case is discriminated on (default: the variant case name as string). We're setting all of the cases to their lowercase equivalent, because that's what the external data looks like.
+
+The end result is clean and zero cost bindings to the external data, in a way that previously would require manual runtime conversion.
+
+Now, let's look at a few more real-world examples.
+
+### Binding to TypeScript enums
+
+```typescript
+// direction.ts
+/** Direction of the action. */
+enum Direction {
+ /** The direction is up. */
+ Up = "UP",
+
+ /** The direction is down. */
+ Down = "DOWN",
+
+ /** The direction is left. */
+ Left = "LEFT",
+
+ /** The direction is right. */
+ Right = "RIGHT",
+}
+
+export const myDirection = Direction.Up;
+```
+
+Previously, you'd be forced to use a polymorphic variant for this if you wanted clean, zero-cost interop:
+
+```rescript
+type direction = [#UP | #DOWN | #LEFT | #RIGHT]
+@module("./direction.js") external myDirection: direction = "myDirection"
+```
+
+Notice a few things:
+
+- We're forced to use the names of the enum payload, meaning it won't fully map to what you'd use in TypeScript
+- There's no way to bring over the documentation strings, because polymorphic variants are structural, so there's no one source definition for them to look for docstrings on. This is true _even_ if you annotate with your explicitly written out polymorphic variant definition.
+
+With the new runtime representation, this is how you'd bind to the above enum instead:
+
+```rescript
+/** Direction of the action. */
+type direction =
+ | /** The direction is up. */
+ @as("UP")
+ Up
+
+ | /** The direction is down. */
+ @as("DOWN")
+ Down
+
+ | /** The direction is left. */
+ @as("LEFT")
+ Left
+
+ | /** The direction is right. */
+ @as("RIGHT")
+ Right
+
+@module("./direction.js") external myDirection: direction = "myDirection"
+```
+
+Now, this maps 100% to the TypeScript code, including letting us bring over the documentation strings so we get a nice editor experience.
+
+### String literals
+
+The same logic is easily applied to string literals from TypeScript, only here the benefit is even larger, because string literals have the same limitations in TypeScript that polymorphic variants have in ReScript.
+
+```typescript
+// direction.ts
+type direction = "UP" | "DOWN" | "LEFT" | "RIGHT";
+```
+
+There's no way to attach documentation strings to string literals in TypeScript, and you only get the actual value to interact with.
+
+With the new customizable variants, you could bind to the above string literal type easily, but add documentation, and change the name you interact with in ReScript. And there's no runtime cost.
+
+### Untagged variants
+
+We've also implemented support for _untagged variants_. This will let you use variants to represent values that are primitives and literals in a way that hasn't been possible before.
+
+We'll explain what this is and why it's useful by showing a number of real world examples. Let's start with a simple one on how we can now represent a heterogenous array.
+
+```rescript
+@unboxed type listItemValue = String(string) | Boolean(bool) | Number(float)
+
+let myArray = [String("Hello"), Boolean(true), Boolean(false), Number(13.37)]
+```
+
+Here, each value will be _unboxed_ at runtime. That means that the variant payload will be all that's left, the variant case name wrapping the payload itself will be stripped out and the payload will be all that remains.
+
+It, therefore, compiles to this JS:
+
+```javascript
+var myArray = ["hello", true, false, 13.37];
+```
+
+This was previously possible to do, leveraging a few tricks, when you didn't need to potentially read the values from the array again in ReScript. But, if you wanted to read back the values, you'd have to do a number of manual steps.
+
+In the above example, reaching back into the values is as simple as pattern matching on them.
+
+Let's look at a few more examples of what untagged variants enable.
+
+### Pattern matching on nullable values
+
+Previously, any value that might be `null` would need to be explicitly converted to an option by using for example `Null.toOption` before you could use pattern matching on it. Here's a typical example of how that could look:
+
+```rescript
+type userAge = {ageNum: Null.t