ReasonReact JSX children–a subtle difference from JavaScript
Tags: reason, react, bucklescript
Published: 2020-04-21 22:26:36

---
title: ReasonReact JSX children–a subtle difference from JavaScript
published: true
description: 
tags: reason,react,bucklescript
---

RECENTLY, I realized that the way we concatenate strings as children elements inside ReasonReact JSX ends up producing subtly different React output than in equivalent JavaScript JSX.

For example, here's a JavaScript React component:

```javascript
function Greet({ name }) {
  return <p>Hello, {name}!</p>
}
```

It desugars to the following:

```javascript
function Greet({ name }) {
  return React.createElement("p", undefined, "Hello, ", name, "!");
}
```

Note how the JSX desugar process breaks up the templated text version into a variadic argument list. There is no string concatenation going on here.

In contrast, what we normally do in ReasonReact is:

```ocaml
module Greet = {
  [@react.component]
  let make = (~name) =>
    <p>{React.string("Hello, " ++ name ++ "!")}</p>;
};
```

This would desugar into something more like:

```javascript
React.createElement("p", undefined, "Hello, " + name + "!");
```

Notice the difference? The element children are no longer a variadic list of strings, but a single concatenated string. To match the JavaScript JSX version, we would have to write the children like:

```ocaml
<p>
  "Hello, "->React.string
  name->React.string
  "!"->React.string
</p>
```

Is this a big deal? Probably not, if you're not doing a lot of string concatenation! Or unless you have some code that introspects element children and behaves differently depending on what the children are.

## ReasonML strongly-typed JSX–a deeper look

The divergence is happening in the ReasonML JSX implementation. In Reason JSX, every element is strongly-typed and there's no built-in interpolation. In JavaScript JSX, for example, you can do:

```javascript
<p>Hello, {name}!</p>
```

But in Reason JSX that's not syntactically valid. The syntax rules say that every JSX element must contain zero or more JSX elements, and some raw text `Hello, {name}!` is not parsed as a JSX element. It needs to be _cast_ into a proper JSX element type somehow. In ReasonReact, that type is `React.element`, and the built-in functions that cast things are:

- `React.string`: casts a string into an element
- `React.array`: casts an array of elements into an element

(There is a [merged PR](https://github.com/reasonml/reason-react/pull/420) to add casts from `int` and `float` to `React.element`, which is legal because of the underlying JavaScript implementation. It should be published in a future release but in the meantime you can implement it yourself if needed.)

So that's why we need do do the explicit casts if we want the same output React components:

```ocaml
<p>
  "Hello, "->React.string // These casts are zero-cost
  name->React.string // Because they are identity externals
  "!"->React.string
</p>
```

Again, this may not actually matter too much in practice. BuckleScript offers a pretty nice way to do string interpolation, built-in:

```ocaml
<p>{j|Hello, $name!|j}->React.string</p>
```

But it's helpful to know in case you ever bump up against unexpected children elements.