# JSX instead of h (createElement) function in widgets and other places

**URL:** https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040
**Category:** Development
**Created:** [1 במאי,‏ 2020,‏ 9:46am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040 "2020-05-01T09:46:39Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [1 במאי,‏ 2020,‏ 9:46am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/1 "2020-05-01T09:46:40Z")

</div>

Hi, I’ve been experimenting with JSX in widgets for better developer experience in themes (I’ve came up with my own setup for theme development without header.html etc, more on that in another topic).

I was wondering why doesn’t Discourse utilise babel plugins within the it’s codebase. In Discourse we have available `transform-react-jsx` babel plugin which can be used to transform JSX to custom createElement function using jsxPragma options. All of that is available in Discousre.

Adding `transform-react-jsx` here [https://github.com/discourse/discourse/blob/master/lib/discourse\_js\_processor.rb#L143-L149](https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149) and somehow exposing the Babel config to change the `jsxPragma` here [https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js](https://github.com/discourse/discourse/blob/master/vendor/assets/javascripts/babel.js)  
could make this happen.

I’ve already implemented the first part, the pragma part still needs some discovery. Unfortunately, there is no `.babelrc` to setup invidual plugin configuration. I get the `React.createElement` function instead of custom function.

More on using JSX without React here: [Using jsx WITHOUT React | r0b blog](https://blog.r0b.io/post/using-jsx-without-react/)

Any thoughts or maybe help how could this be achieved?

UPDATE:  
I did manage to make this work by changing the pragma setting in `vendor/assets/javascripts/babel.js` on line 26586

```js
var id = state.opts.pragma || "React.createElement";
// to
var id = state.opts.pragma || "h";

```

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [1 במאי,‏ 2020,‏ 10:09am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/2 "2020-05-01T10:09:53Z")

</div>

Have you see this? You can use Templates now if you don’t like Hyperscript:

> [@Is it just me, or is Hyperscript hideous?](https://meta.discourse.org/t/is-it-just-me-or-is-hyperscript-hideous/131635/21):
>
> I will give a more detailed example in few days, but I can confirm it works. import hbs from 'discourse/widgets/hbs-compiler'; import { createWidget } from "discourse/widgets/widget"; export default createWidget("foo", { template: hbs` \<p\>MY TEMPLATE\</p\> ` }); Used in a post with [wrap] and WidgetGlue:

[https://github.com/discourse/discourse/commit/dffb1fc4ee8a4d58f48145decb1590a304e8cf7d](https://github.com/discourse/discourse/commit/dffb1fc4ee8a4d58f48145decb1590a304e8cf7d)

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [1 במאי,‏ 2020,‏ 11:28am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/3 "2020-05-01T11:28:39Z")

</div>

@merefield yes, I know about `hbs`, but I’m not “your typical discourse theme developer” 🙂 . I would like to use power of JSX which is actual JS instead of `hbs` which are limited in their functionality.

And did managed to use JSX in discourse theme now. But in my own discourse fork, so not very useful for general theme development.

---

<div class="post-metadata">

### Author: ![j.jaffeux](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/j.jaffeux/32/60297_2.png) [@j.jaffeux](https://meta.discourse.org/u/j.jaffeux)
#### Post date: [1 במאי,‏ 2020,‏ 12:16pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/4 "2020-05-01T12:16:40Z")

</div>

Ultimately it’s @eviltrout’s call. I can give you my feeling though. Yes JSX can be pleasant to use and we could have it in Discourse, however I don’t think we will, because we would have to support it and we are more leaning towards getting rid of widgets at some point than adding more options.

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [1 במאי,‏ 2020,‏ 12:22pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/5 "2020-05-01T12:22:08Z")

</div>

I completely understand your point. I’m just wonderinb, why not open the configuration for babel (and bunch of other stuff) for theme and plugin developers?

For example `.babelrc` in the theme folder can be used for some addition options for js transpiler. If the `.babelrc` is present `discourse_js_processor` can use it and extend it’s babel config and enable bunch of additional JS features.

Just an idea.

---

<div class="post-metadata">

### Author: ![j.jaffeux](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/j.jaffeux/32/60297_2.png) [@j.jaffeux](https://meta.discourse.org/u/j.jaffeux)
#### Post date: [1 במאי,‏ 2020,‏ 12:27pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/6 "2020-05-01T12:27:06Z")

</div>

It’s just a matter of “public api” in a broad mean, whatever we allow, we will have to carry it. What seems easy to support today, might be hard tomorrow. We are definitely experimenting with a lot of things in the pipeline and the client side part of the app lately. Let’s see what robin thinks of all of this.

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [1 במאי,‏ 2020,‏ 12:33pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/7 "2020-05-01T12:33:02Z")

</div>

Please feel no pressure, I’m just a curious developer with some questions. I came to discourse from full stack JS (Node/Vue) world where Ember is just not an option, especially when you have React or even better, Vue.

Ember feels so unnatural and `handlebars` are just out of date with it’s features. Templating power of JSX and especially Vue SFC cannot be compered with something basic like `hbs`.

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [1 במאי,‏ 2020,‏ 3:02pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/8 "2020-05-01T15:02:00Z")

</div>

I disagree that Ember is not an option for full stack JS. I understand the argument that Ember is not as popular as other frameworks and you might want to choose another framework for that reason, but there are certainly many many sites using node on the back end and Ember in the front end. All of Ember’s tooling is based on Node, after all.

Having said that, JSX is not in Discourse core’s future, but if there are things we could do to enable its usage in plugins I’d be open to it. Please make a proposal for how it would work!

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [1 במאי,‏ 2020,‏ 7:20pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/9 "2020-05-01T19:20:05Z")

</div>

> [@eviltrout](#):
>
> I disagree that Ember is not an option for full stack JS.

It is an option, but in my development and work surrounding it is not, and I personally would not use it because Vue seems like a natural fit for these kind of applications.

> [@eviltrout](#):
>
> Having said that, JSX is not in Discourse core’s future, but if there are things we could do to enable its usage in plugins I’d be open to it. Please make a proposal for how it would work!

Just a bit of context:

In last couple of months I’ve been working on developing a pretty custom theme for a client, where l learned a bunch of stuff about inner workings of discourse, but that’s probably a small percentage.

Currently, I’m refactoring bunch of beginner mistakes and trying to make things friendly as possible for future colleagues that could end up maintaining my work. That’s way, I did a bit of experimentation regarding JSX.

Today, I tried the approach from the first post in topic. I’ve included `transform-react-jsx` here: [https://github.com/discourse/discourse/blob/master/lib/discourse\_js\_processor.rb#L143-L149:](https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L143-L149:)

```plaintext
      if opts[:module_name] && !@skip_module
        filename = opts[:filename] || 'unknown'
        "Babel.transform(#{js_source}, { moduleId: '#{opts[:module_name]}', filename: '#{filename}', ast: false, presets: ['es2015'], plugins: [['transform-es2015-modules-amd', {noInterop: true}], 'transform-decorators-legacy', 'transform-react-jsx', exports.WidgetHbsCompiler] }).code"
      else
        "Babel.transform(#{js_source}, { ast: false, plugins: ['check-es2015-constants', 'transform-es2015-arrow-functions', 'transform-es2015-block-scoped-functions', 'transform-es2015-block-scoping', 'transform-es2015-classes', 'transform-es2015-computed-properties', 'transform-es2015-destructuring', 'transform-es2015-duplicate-keys', 'transform-es2015-for-of', 'transform-es2015-function-name', 'transform-es2015-literals', 'transform-es2015-object-super', 'transform-es2015-parameters', 'transform-es2015-shorthand-properties', 'transform-es2015-spread', 'transform-es2015-sticky-regex', 'transform-es2015-template-literals', 'transform-es2015-typeof-symbol', 'transform-es2015-unicode-regex', 'transform-regenerator', 'transform-decorators-legacy', 'transform-react-jsx',exports.WidgetHbsCompiler] }).code"
      end

```

And changed the pragma setting in `vendor/assets/javascripts/babel.js` on line 26586

```plaintext
var id = state.opts.pragma || "React.createElement";
// to
var id = state.opts.pragma || "h";

```

That has enabled me to use `h` function from `virtual-dom` instead of `React.createElement` in the babelfied code. `h` is part of discourse and this felt like a natural fit.

I don’t know the history of discourse development, and you probably had your reasons for adding fullblown babel.js file with bunch of useful plugins in the code. It’s a shame that these plugins are not available to end developer user like me. So my proposal would be to:

1. use some kind of babel config, .babelrc, .babel.yml, or in `about.json` define a babel config field like in `package.json` [Configure Babel · Babel](https://babeljs.io/docs/en/configuration#packagejson). That config could reference all these plugins in babel.js (or maybe some others, check 3.)

2. In `discourse_js_processor.rb#babel_parse` `[https://github.com/discourse/discourse/blob/master/lib/discourse\_js\_processor.rb#L138](https://github.com/discourse/discourse/blob/master/lib/discourse_js_processor.rb#L138) read this config and extend it the current config.

3. If a developer wants to have an custom babel plugin, he could add it to it’s `vendor/assets/javascripts/babel/plugins` folder (or just `babel/plugins` folder) and discourse would pick it up and us it in transpile process.

I’m not experienced in Rails backend development - so this is my meta proposal.

And another question, while we’re at it, how hard would it be for discourse to read the dependencies from lets say themes `package.json` and installed them on first run and use the (have them cached or something like that) inside of theme? (If dependencies change, check which changed and install or remove them, etc).

Once again, you guys are doing a great job and your work is amazing. Please, don’t feel any pressure from my side, I’m just here for learning and from some good discussion and sharing opinions and ideas. I do not expect from you to implement my requests nor anything.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [4 במאי,‏ 2020,‏ 8:39am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/10 "2020-05-04T08:39:13Z")

</div>

> [@zcuric](#):
>
> and `handlebars` are just out of date with it’s features.

What do you mean by that? What is not possible with handlebars and Ember that’s implemented in another framework?

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [4 במאי,‏ 2020,‏ 10:11am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/11 "2020-05-04T10:11:19Z")

</div>

What I meant is the JSX is JS, not a templating language, a being JS by itself makes is more powerful (for example: ternary operator, map function, etc). You can make what ever you want in Ember/Handlebars but JSX is more convenient and developer friendly, IMHO.

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [4 במאי,‏ 2020,‏ 7:37pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/12 "2020-05-04T19:37:57Z")

</div>

> [@zcuric](#):
>
> That has enabled me to use `h` function from `virtual-dom` instead of `React.createElement` in the babelfied code. `h` is part of discourse and this felt like a natural fit.

And did that work? You were able to add jsx and everything was fine?

Now the other questions you had about why babel is configured the way it is - Discourse is built on Rails using the asset pipeline, and webpack support was only added into Rails in version 6. Discourse’s code base is 8 years old! So over time we’ve added stuff to it but we’ve outgrown its limitations.

One of the big projects this year is migrating to [Ember CLI](https://ember-cli.com/) - if you look at our commits you’ll see work towards this goal going into Discourse, but it’s a long road.

Once that is in place, you will be able to use a lot more Javascript build features that currently do not work unless you run them from within Ruby, which is cumbersome. In the meantime, I can’t see us devoting time to reading `package.json` and having that go in the Rails pipeline automatically.

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [5 במאי,‏ 2020,‏ 11:03am UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/13 "2020-05-05T11:03:39Z")

</div>

> [@eviltrout](#):
>
> And did that work? You were able to add jsx and everything was fine?

I’ll POC PR to demonstrate how that works.

> [@eviltrout](#):
>
> Now the other questions you had about why babel is configured the way it is - Discourse is built on Rails using the asset pipeline, and webpack support was only added into Rails in version 6. Discourse’s code base is 8 years old! So over time we’ve added stuff to it but we’ve outgrown its limitations.
> 
> One of the big projects this year is migrating to [Ember CLI](https://ember-cli.com/) - if you look at our commits you’ll see work towards this goal going into Discourse, but it’s a long road.
> 
> Once that is in place, you will be able to use a lot more Javascript build features that currently do not work unless you run them from within Ruby, which is cumbersome. In the meantime, I can’t see us devoting time to reading `package.json` and having that go in the Rails pipeline automatically.

Thanks for the insight and explanation!

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [10 במאי,‏ 2020,‏ 1:38pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/14 "2020-05-10T13:38:50Z")

</div>

An update - I’ve managed to make a `ThemeField` babel, which is used to read `babel.config.json` from theme and inject plugins into `Babel.transform` in the `discourse_js_processor`.

I hope go get the PR out soon just to show as a prof of concept. A warning - It will be a huge mess 😅.

PS. Ruby is so easy to work with 🤩

---

<div class="post-metadata">

### Author: ![zcuric](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zcuric/32/292837_2.png) [@zcuric](https://meta.discourse.org/u/zcuric)
#### Post date: [10 במאי,‏ 2020,‏ 4:31pm UTC](https://meta.discourse.org/t/jsx-instead-of-h-createelement-function-in-widgets-and-other-places/150040/15 "2020-05-10T16:31:04Z")

</div>

Here it is:

[https://github.com/discourse/discourse/pull/9729](https://github.com/discourse/discourse/pull/9729)
