Hacker News new | past | comments | ask | show | jobs | submit login
Alternatives to JSX (bloomca.me)
104 points by bloomca 26 days ago | hide | past | web | favorite | 124 comments



Wow, after reading this, I'd be surprised if anyone could come away with the conclusion "Yeah, that's better than JSX."

I think the general distaste for JMX is that it "feels" backwards initially: it mixes in HTML snippets within code, while many people are used to template files, which are mainly markup with small bits of "presentational" code injected.

But once you realize and really understand that JSX is just shorthand for javascript calls (and the article does a good job of explaining this), you hopefully eventually reach an "Aha!" moment of thinking of JSX in terms of underlying component creation. That's what happened to me, anyway,.


I've always thought the "HTML in JS" was in fact a strawman argument set up to deviate the attention from the fact that it is indeed a different syntax for function calls (and class instantiation). And it is actually that the thing that I dislike about JSX. I think it's a bad syntax for function calls/class instantiation.

I do like hyperscript better than JSX... precisely because it's not a syntax for function calls. Because it's just a function.


It's not entirely a strawman, because there are legitimate concerns about separation of concerns that the "HTML in JS" argument worries about. Especially in the web world which still knows the legacy against fights of business logic in templates going back to "bad old PHP days", etc.

There are good reasons to be skeptical about JSX simply because of the worry about how much business logic may accidentally be embedded directly into views. For JSX, the answer is typically to solve that in other areas of workflow and patterns of habit. But the war between "as dumb as possible a templating language" and "just let me use my real language for templates" will probably wage on in the web space forever.


Maybe it's best not to think of React as a View but a Component... You can create Components that are simple Views and render what they are given, you can also create smarter Components that are more in line with Controllers.

As a loop: Store -> State -> Reducer -> Component -> Action -> Dispatch -> Store ...

Or if you want something closer to MVC, you could use MobX or similar.


I've been sold on JSX for a while, personally, and pretty much have that architecture in my apps I'm working on.

I just also remember being an idealistic Django-loving youth that wanted the dumbest possible template engine to avoid teenage PHP drama, so I definitely sympathize with folks whose gut reaction is upset with JSX for crossing the streams (again), and thought to make sure it was clear that it isn't a straw man argument to many. (Even if it is also, as in my case, an argument that can be won in favor of JSX. Though also in my case one of the big factors winning that argument was TSX [Typescript type checking of JSX views] versus many of the alternatives and their [lack of] static checking/analysis.)


> I do like hyperscript better than JSX... precisely because it's not a syntax for function calls. Because it's just a function.

You should check out Elm: https://elm-lang.org/. It uses a hyperscript like syntax for rendering in a functional style. It's all just functions, and it reads that way too.


It's not just that. The abstraction placed between a Node and an Element is very important and crucial for React (and for a good reason, this is similar to a well-known programming paradigm used for 3D games). I'd say there is a significant semantic difference that can be simulated by a function call (you can simulate nearly any statement).


Hyperscript roughly equals React.createElement, JSX transpiles the same for either pragma.

JSX is just a more natural syntax to those used to HTML/XML directly as an expressive syntax. Nobody is forcing anyone to use it... `const h = React.createElement;` if you prefer the functional syntax directly.


When I was first introduced to JSX I found it very confusing because it was sold to me as "HTML in Javascript" but it's... really not. In particular these things threw me off:

  - You need to close tags you wouldn't need to close in HTML (breaks copy+pasted HTML)
  - Argument quoting isn't optional (breaks copy+pasted HTML)
  - HTML style Comments aren't allowed (breaks copy+pasted HTML)
  - Whitespace gets stripped for all elements (breaks PREs)
  - There's a JSX-specific syntax for "fragments" that seem to only exist for parser reasons?
  - Variable references work inside of text nodes but don't inside of attributes.
After I googled a bunch and read the spec it helped a bit, but it left a bit of a bad taste in my mouth. I'm still left scratching my head over why you should be able to use an element as the value for an attribute though, like this:

  <foo a=<bar> test </bar>/>


The "X" stands for "XML", and these things are all XML:

  - You need to close tags you wouldn't need to close in HTML (breaks copy+pasted HTML)
  - Argument quoting isn't optional (breaks copy+pasted HTML)
  - HTML style Comments aren't allowed (breaks copy+pasted HTML)
  - Whitespace gets stripped for all elements (breaks PREs)
JSX is based on XML rather than HTML because XML is a version of HTML with all the backwards-compatibility weirdness stripped out, which makes it easier to parse. If you've learned XHTML, JSX isn't very hard.

The stuff about <pre> elements and elements as values are pretty easy if you remember that all attributes are just JavaScript values, and the contents of a JSX element is just the "children" attribute.

In other words, these are the same:

    <foo>{bar}</foo>
    <foo children={bar} />
And `bar` is just a JavaScript value - a JSX fragment, a string, a number, whatever you want it to be.


I don’t really care how easy it is to parse for a machine. I want it to be easy to parse for me. And HTML5 is plenty easy. GP is right, JSX is a weird bastard child of two languages that is very awkward.


I find it difficult to parse for me when a tag doesn't end. The browser has a very specialized parser that can figure out if a ul after a p is inside it or is the p implicitly closed at that point, but I can't figure that out myself. So I welcome the X in XHTML, it keeps the syntax sane.


To add on to hiccuphippo's comment:

By "easier to parse", I mean "easier to parse for humans as well as computers".

I, a human, would also prefer not to memorize the list of self-closing tags, or the arcane rules for when <p> and <li> autoclose. Not having to do these things _blatantly_ makes XHTML more readable.


Agreed. I was just reading about when to close tags and when you shouldn't... well... it seems arbitrary to say the least. I would rather just close ALL tags, no exceptions.


[flagged]


What enraged_camel said. Machine code is easy to parse. Yet we don’t optimize our tools for that. Readable code is more important than parsable code in most cases.


This sounds like a very bizarre strawman version of what the parent is saying.

No, obviously the desire is not to write code like one writes natural language. But ease of reading is an extremely important quality to have in programming languages. Programmers read code way more often than they write it, and them being able to make sense of it is crucial.


3. and 4. are valid in XML.


Oh, huh, I didn't know XML uses the same comments as HTML.

4 is valid, though: XML requires special directives to preserve whitespace. JSX requires different syntax with {`...`}, but it's a similar idea.


> XML requires special directives to preserve whitespace

Not in what you would consider content: from http://usingxml.com/Basics/XmlSpace: "White space in any other location must be passed on to the processing application, according to the XML specification."


Elements as values of attributes are extremely useful in custom components.

    <FancyArticle heading={<h1>...</h1>} body={<main>...</main>} />


Other systems address that by providing a <template>, # include or {% block %} element that allow you to state where/what things should be inserted.

I dislike the attribute way because:

- attributes are used for everything. It seems nice and simple - one single way to do all things ! - but it means when you see something passed this way, you never know what it's for without diving into the code or doc. There is no convention. Is it data ? Is it a children ? Is it a callback ? If yes, what for ?

- it allows people to make big inline blobs of code. And as often, when something ugly is easy on the short run, and the clean things is hard, they do the easy thing. JS is full of those stuff, and I wish we would not add new ones in the tooling we use.

- The syntax is so unatural when you have been doing HTML for decades. I understand the desire not to be limited by legacy, but here it's not working around any limit. There are alternatives. So we just break habits to break habits. Again something quite common in modern JS: let's replace the wheel by a different one. Not better. Just different. Because, reasons.

- It's easy to get wrong. Case in point, your comment and the parent don't even have the same syntax because one of you couldn't, from the top of the head, come up with the proper one.


"Case in point, your comment and the parent don't even have the same syntax because one of you couldn't, from the top of the head, come up with the proper one."

I'm not sure what you mean by this. They're both valid. I don't find the one enclosed in {} nearly as confusing, because once you're inside curly braces you can have arbitrary javascript expressions so all bets are off.

The thing that's strange is being able to have a JSX Element as an attribute value without enclosing curly braces. Perhaps the use case of passing arguments to custom elements is more common than I thought thought.

Specifically "<foo a=<bar/>/>" is weird.


FWIW, I've never actually seen that in real-world React code-- neither in libraries I've used, nor in my own code. It's more common (and much more natural) to see JSX Elements passed as children to other components, like you see with a react-bootstrap Panel, for example:

    <Panel>
      <Panel.Heading>Foobar</Panel.Heading>
      <Panel.Body>
         Panel body contents
      </Panel.Body>
    <Panel>
Which is itself just passing the Panel.Heading and Panel.Body elements as an array to Panel's children prop, but it's much more natural than having some arbitrarily complex hierarchy of elements passed in another prop.


Huh. I've been using React for several years and am deeply involved in the community, and this is the first time I've ever seen a JSX element passed as a prop without enclosing curlies.

In other words, I've seen this:

    <SomeComponent leftPanel={<LeftPane />} />
but I didn't even know you could do this:

    <SomeComponent leftPanel=<LeftPane />  />
Small quirk of JSX syntax that almost no one knows exists, I guess.


I personally have never passed a JSX element through an attribute, and I don't see what the use case would be. It smells like an anti-pattern. Why wouldn't you use a render function that returns an element, or just pass the component and create the element in the child?


The places I see it the most are in routing and access control components. Here's a convoluted example in my router, not common throughout the application.

      <Route
        path="/petition/:petitionId/page/next"
        render={({ match }) => (
          <InRoles
            roles={Roles.pageReviewer}
            deny={`/`}
            allow={PS(match.params.petitionId, SECTION.PAGES, null, ({ petitionId }) => (
              <PageReviewNextPage petition={petitionId} page={'-'} direction={1} />
            ))}
          />
        )}
      />
Route comes from react-router. InRoles is internal to the application and accepts a string or function/component... if it's a string it redirects as appropriate, or renders. if null/empty just hides the result. PS is a function that returns a nested/common component structure.

A more simple example would be...

    <Fragment>
      <Switch>
        <Route exact path="/" render={({ location }) => <Home search={location.search} />} />


Not sure what toolchain you're using. I'm using the typescript compiler with Mithril and whitespace is preserved for this code:

  <pre class="debug">{JSON.stringify(store, null, 4)}</pre>


I think they are referring to literal text/whitespace in the template. I don't know about other implementations, but typescript mutates the whitespace if newlines are involved.

It seems that the rules are, for any run of literal text:

- preserve runs of whitespace that don't contain newlines - drop initial and final whitespace if it contains a newline - collapse any internal runs of whitespace containing newlines to a single space.

(based on a quick experiment with tsc.)


That's different than preserving:

  <pre>

  foo</pre>


The author doesn’t do justice to hyperscript. Hyperscript’s one-to-one mapping with CSS selectors is great for readability while remaining terse. For example:

  let value = ‘example’;
  let x = Math.random();
  h(‘form.Foo[action=/whatever]’,
    h(‘input.Bar[type=text]’, {
      value,
      oninput(event) {
        value = event.target.value;
      }
    }),
    h(‘button.button-small.button-primary[type=submit]’,
      (x > 0.5) ?
        h(‘strong’, ‘Submit’) :
        h(‘em’, Submit)
    )
  )

vs.

  let value = ‘example’;
  let x = Math.random();
  <form className=“Foo” action=“/whatever”>
    <input
      className=“Bar”
      type=“text”
      placeholder={}
      value={value}
      onInput={
        (e) => value = e.target.value
      }
    />
    <button className=“button-small button-primary” type=“submit”>
      {
        (x > 0.5) ?
          <strong>Submit</strong> :
          <em>Submit</em>
      }
    </button>
  </form>
Although the example is contrived, it highlights several of my pain points from when I used react/jsx.

The equivalent jsx requires transpilation, dipping back into js via {}, is far more verbose, and is particularly annoying when logic inevitably becomes part of your template. Whereas hyperscript style js solutions to view templating have straightforward access to loops, array methods, ternaries, objects, functions, etc. jsx requires you to either reinvent js functionality as components or switch back and forth between contexts to use js in your jsx template.

Hyperscript also nicely separates static vs dynamic properties in elements, making understanding a large code base easier. All the static stuff is in the selector, all dynamic stuff is in the optional second argument. There’s no equivalent in jsx.

As an aside, React’s hyperscript is kind of a pain to write on its own and there are many good reasons to stick with jsx if you’re using React. (Last I checked, React was strict about the second argument being an options object or null and the third argument being specifically an array of children.) My arguments in favor of hyperscript are most applicable if you’re using a library that was intended to be used with hyperscript, like Mithril or snabbdom.


One nice thing about JSX is the tooling/linting available for it. For example, tooling with warn when you have target=_blank without rel=noopener or whether you've defined the necessary attributes for each tag. It's one thing that got me using JSX years ago regardless of how I felt about JSX.

Does the same level of tooling exists for hyperscript seeing that it's so much more string-based?


> Wow, after reading this, I'd be surprised if anyone could come away with the conclusion "Yeah, that's better than JSX."

IDK, hyperscript (or even just aliased createElement) is enjoyable, not that verbose, and not needing a compilation pipeline just for JSX is nice. As in, I want to drop in an interactive bit inside a wider static page, dropping in react's JS (or linking in a CDN copy) and using createElement is way more convenient than having to set up a compilation pipeline.


I'm using webpack/babel and ES6 syntax anyway, so the transpile doesn't bug me. It's also very close to E4X which I really wanted for a very long time, but never gained acceptance outside of ActionScript 3 (Adobe) and Mozilla. VB.Net also has a similar literal syntax).


Yeah, usually the initial dislike to JSX is because of bad, past experiences of people mixing view code with domain logic, storage, often SQL. But once people realize that React handles only view - some XML in there makes a lot of sense, since HTML/XML is core object in the view domain.


I don't think anybody would argue that the alternatives are better than JSX (if it were, why use JSX at all?), the step that you want to avoid is transpiling jsx. Why? Javascript's tool chain is complex, once you're using jsx you probable want `react-create-app` too, and things begin cascading from there. It's only worth it if what you're doing has a certain degree of complexity.


If you dislike JS in the first place, having it also into your presentation layer isn't seen as an advantage.

Plus, many people consider the presentation like something that should be limited and stay that way. Having a turing complete language in it defeat that purpose.

But I agree, the alternative presented in the article don't address those issues at all so I can't see advantages over JSX.


If you dislike JS in the first place, you're probably not going to be doing much with React to begin with.


You’re right that none of these alternative ways of writing JSX syntax are better than JSX. But I think you miss why people don’t like JSX in the first place;

Having to compile your javascript sucks.

If your framework requires me to have a build system in place just to write some javascript, I’m either not going to use it or, if forced, I’ll find whatever way I need to to get the code written without having to stand up a giant build system.

Naturally, the true solution for people like me is to use Vue or Angular or another library that’s not as intrusive as React. But sometimes we’re forced, so we use that terrible createElement syntax. And come away disliking React even more.


Setting up a build system is not required, React with JSX can be inlined using plain old <script> tags just fine I believe.

  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="text/babel">
      // React with JSX goes here
    </script>
  </body>
It it is less performant than compiled setup, of course.


The React docs point to a much more recent example of this setup:

https://raw.githubusercontent.com/reactjs/reactjs.org/master...

And have actual instructions here:

https://reactjs.org/docs/add-react-to-a-website.html


I objected to it at first.

Then I learned how much nicer Javascript is with Babel.

No worrying about browser compatibility, Babel just makes it all work.

If I see some stage 3 proposal that I want to use, no problem, drop it in.

When I wanted to start moving over to Typescript, one line in Babel and I could start writing .ts files.

While learning what in the world was going in with regards to Babel was a bit of a painful start, now that I have it up and running, life is good.


Using Angular or Vue without a module system sounds awful. How do you organise your code? Manual namespacing on the global object?

So I'm coming from a perspective where I will definitely be compiling my JavaScript regardless, and adding JSX doesn't make much difference complexity wise (and provides a much nicer syntax).


Can't speak for Vue, but Angular has its own module system in addition to ES imports, which does give a gain when it comes to testing.

There is one benefit to Angular's templates that is pretty nice due to its use of TypeScript - you get intellisense in editors on your templates.


JSX is typed, you get all the benefits of intellisense and type safety. It even supports generics.


ES modules with relative URLs have shipped in browsers, and when import maps [1] land, it could become viable to not bundle or transpile for local development and only do so for production builds.

1: https://github.com/WICG/import-maps


Having to set up a build system is a bit of a pain. The value I get from being able to break down the UI in components and with the possibility of testing them independently makes it worthwhile for me.


I think people might just work on projects smaller than I'm used to. I set up a build on day one of a project and then just it for the next six months or year or whatever, so I really never see it as a pain point. As you say, well worth it for all of the benefits.

Although frankly if I was doing lots of small projects I'm sure I'd have one of the many starter projects set up how I like it and it would just be running a script once.


>Having to compile your javascript sucks.

That's why I don't like typescript :) Also because typescript makes things way less understandable than without type annotations


>makes things way less understandable than without type annotations

Strange proposition. No dev I know who has professionally worked with both would claim the same, even the skeptics long term ended up grokking types as a way to make it easier to understand the intent behind a piece of code, even when they prefer JS for writing, never for reading other people's code.

How long have you worked with each? What editor tools have you used for TS? I'm really curious about your experience.


I was investigating typescript to use in a react native project and while checking some of the definitions contained in a package in Visual Studio Code (I believe it was react-navigation) the way they were presented was so confusing that I aborted immediately going with typescript. It was a function that returned a function that accepted as a parameter another function and the extra syntax was really taxing to my brain to fully understand what the function was all about. When I removed the extra syntax it was way clearer for me to understand it. Sometimes, pure javascript with a good variable naming is better. Sure, I could use some of static type checking to avoid some bugs but there weren't many bugs that I remember when I was doing the app so I think it didn't impact me too much. Also I am a solo developer in my company, I don't work in a team so it's not a big problem here.


>It was a function that returned a function that accepted as a parameter another function and the extra syntax was really taxing to my brain to fully understand what the function was all about. When I removed the extra syntax it was way clearer for me to understand it.

Interesting. Was that your first time reading TS? Higher order functions, specially when combined with optional arguments, in JavaScript are an easy source of bugs for me but never when using TS, since I can simply see what I'm being asked for and getting back.

>Also I am a solo developer in my company

I actually forgot that scenario. Yes, unless you have adapted to the type-driven development way of thinking you will hardly see many of the benefits of maintaining code that has types vs code that does not.

When you're the one that wrote the code, you'll have an unfair advantage at understanding how the author meant the code to be used. When you don't have that luxury, types can be a great aid in most situations. Of course, it's a question of time and codebase size until you break your own preconditions when changing something, but you'll not notice how the type system can save you from that until it you actually learn how to take advantage of it.

It's a catch-22, so if you're curious and want to see what's meant by "taking advantage of the type system", I can only suggest you that you give something like Elm a try. There you'll see many times in the first couple of weeks what types can do for you when changing code.


I have developed in many static typed languages like C# and Java in my career but now I am being converted to web and mobile development so I know the advantages and disadvantages of a static type system. In my experience, I am much more productive in a dynamic instead of a static type system when implementing frontends. For you to have an idea, this react native project I developed alone was sold to the client to be implemented in 3 months. The catch was I didn't have any experience in mobile development and I was still developing another web project. So I had to learn react native, ios and android development as fast as possible to do a medium-sized app. Having more friction using typescript for me was a killer.

But for a backend I would certainly have used static types as it's way more critical.


RN and Typescript is interesting.

The getting started, learning curve, and "wtf is happening here" curve is rather high. Especially once you add in Redux and React Navigation.

It doesn't help that all of the "getting started with RN and TS!" assume a decent understanding of TS.

I eventually got TSX working, but yeah, figuring out what you need to declare to make it all work is not easy.

Redux having 5 major ways (or whatever it is) of using it didn't help any. I use the shorthand form of mapDispatchToProps in my connect call, which none of the "TS in Redux" tutorials seem to use. (Why not? It is so much easier and shorter!)

I eventually gave up on typing Redux, IMHO the last thing Redux needs is more boilerplate! I rarely have type problems in my Redux code anyway. I probably understand enough to do it now (or maybe not, haven't investigated what typing redux-thunk looks like), but I'm going for "less bugs" not ideological purity in my code.

The tl;dr is that you have to declare an interface for your props, an interface for your state, and your props interface has to include a NavigationScreenProp which I honestly declared as

        navigation: NavigationScreenProp<any>;
because I have better things to do in life than spend yet more time trying to figure out how to type something I never actually use. (state is in Redux, so the NavigationScreenProp is of very little use to me)

All that said, the one time learning curve was worth it.

Typescript definitions can get super complicated though, my 90% use case is preventing typos and allowing for refactoring, so I only type objects I pass around as interfaces and I type functions that have been the source of bugs. Mostly anything that comes from my backend DB I want typed end to end. The majority of my REST endpoints use Typescript to pull from the DB so I just share interfaces across my backend and frontend.

It has been worth the hassle just for that.


I think you are confusing "less understandable" with unfamiliar.


That makes sense, it's like an HTML-esque DSL in JavaScript. I'm learning Vue recently, and it seems that Vue templates are also rendered in a JS function. This is perhaps similar to JSX, but the semantics seem closer to HTML.

Are there other frontend component libraries that embrace web standard HTML and CSS, and in particular Web Components?


Yep, with Vue single file components you can paste in your plain HTML (and css/sass) and it will just work without having to be properly formatted XML, as long as there is a single root.


So JSX is like pure php templates.


I think it is closer to .Net's WPF. The XML is treated as another way to write objects with attributes, children and bindings. It's more clear for writing tree-ish stuff than pure code.


I remember reading somewhere that React was first written because someone wanted something similar to PHP workflow, but I don't see it, probably because I've only had a 1 month experience with PHP one time.


Hack actually supported it directly with XHP, which was great. Native support for extensible XML and data escaping out of the box is something PHP should have done as soon as was feasible, but unfortunately the best PHP can still do is string concatenation. Even Twig, which is basically an entire DSL with its own lexer, parser and everything, eventually just mashes strings together.

I really wish XHP had been on the list of features PHP7 had taken from Hack and that it was default. Of course you can compile it in as a plugin, but that doesn't help the majority of PHP users nor does it encourage developers.


Indeed, JSX can be traced back to Facebook's idea of XML syntax on top of PHP called XHP [1][2]. It was never adopted by the community and did not break out of FB's custom PHP runtime called HHVM.

  [1] https://www.facebook.com/notes/facebook-engineering/xhp-a-new-way-to-write-php/294003943919/ 
  [2] https://docs.hhvm.com/hack/XHP/introduction


You can mix php and html. A minimal php hello world would just be "Hello World" in a file.

I expect a templating engine equivalent to php's twig becoming the standard way to separate the views from the behavior soon enough.


There is a javascript fork of Twig that I used to play around with[0] but it's far from "standard."

[0]https://github.com/twigjs/twig.js


I hope/suspect transpilation will not be a given any more in the future. In that scenario, it's good to have alternatives to JSX.


I used to be really sceptical about JSX, 1 extra transp. step, anorthodox use of xml in JS, etc.

Having been using it for the past 3-4 years I think its an above agerage elegant solution when it comes to templating solutions.


Why JSXBelPack when you can

  import { html, render } from 'https://unpkg.com/lit-html';

  const benefitTpl = ({name, desc}) => html`
    <dt>${name}</dt>
    <dd>${desc}</dd>
  `;
  
  render(html`
    <lit-rocks>
      <dl>${benefits.map(benefitTpl)}</dl>
    </lit-rocks>`, document.body)

And updates are fast without any VDOM overhead.


I'm popping in here to say, I've had people from both Vue and React join my team where we're using lit-html. Transitions have been seamless, mental models map over quite well.


There's so much less cruft and overhead with lit-html, and you get speed that's akin to vanilla JavaScript. As for JSX, I think we can all get used to something non-standard, which after enough time feels normal, but the benefits of a lit-html are so immediately apparent. The transition is super easy.


Yes, this!

Also, don't forget your `?module` at the end of your unpkg link to get that running


Is this doing a full re-render on each change? Having not seen the state management piece... but could be nice for progressive enhancement modules in a server-rendered app. I've done similar in JS.


Nope! It doesn't visit each node like vdom. VDOM visits every virtual node. lit-html looks at only the expressions. That's where a lot of the speed/performance comes from


zeptime's correct. lit-html places markers in the HTML where the expressions are. Then it renders the HTML once, and updates the expressions directly. That's how it avoids a full re-render and avoids building am in-memory VDOM and doing any diffing.


Pug is a worthy mention. https://github.com/pugjs/babel-plugin-transform-react-pug/bl... wasn't mentioned


What really bugs me about JSX is the complexity it introduces to the build pipeline - which might be fine for senior developers, but I regularly see junior developers struggle with configuration and errors.


You get a ton of other benefits from buying into a webpack-style build system though:

- Modules - TypeScript - Newer JavaScript syntax - SASS compilation - Code splitting

etc.

For me, it's hard to imagine a medium/large sized JS project without using some kind of module system. And once you've setup the build system for that, you might as well use all of the other features that it enables.


You can get modules now in many browsers, as well as the latest JS syntax, without needing any build step.

Code splitting/vendoring has changed with HTTP2, and will continue to change as HTTP3 comes along.

SASS compilation, and if you choose to use TypeScript, will require a build step, but at least the SASS side of that is very easy and straight forward by itself.


You're still missing a few of the finer points of stage 0-3 optiosn for babel. Also, that bloats out to a lot of requests with full round-trip time, and no real good server solutions for bundling via HTTP2/3 push yet.


To be fair, I see junior devs struggle with all build tools.


This is true, and further to this point, I don't think there is or should be any crossover between 'junior' and 'configuring build pipelines'. To me that's by definition a task for a senior Engineer.

Put another way, if you have a skillset that includes configuring and/or maintaining non-trivial build pipelines, you're not a junior Engineer any more.


Which is why as a Senior Engineer, some of the best productivity you can build for your team is to be able tell a Junior when starting a new application, "clone this template, npm start to debug it, it uses Typescript, you'll learn it as you work with it I promise, just let me know when you need help with something complex, and I'll be here to help you through all your PRs". (Or F5 to start in something like Visual Studio, depending on familiarity levels of Juniors you are trying to work with.)

Junior developers are often just fine trusting build systems as block boxes once you've got them setup for them.


We use virtual jade at work. It works with most virtual doms that use some form of h(name, attrs, children) calls.

We have webpack hot reloading so we can work on UI without any page refreshes.

It’s been very productive. I love the brevity of jade/pug. Jsx feels like a lot of boilerplate braces and ending tags.

https://github.com/tdumitrescu/virtual-jade


I've been using pug with the babel plugin for a React project, and I really like it. I'm a fan of Haml for server-side rendered Rails views, too. I'm surprised pug isn't more widely used in front-end dev, since it is so popular in Node apps.

Also: projects are still using the "jade" name? I thought they were forced to change?


pug is really nice (as is haml).

The only reason I bear with the superfluous verbosity of JSX is because it plays well with typescript. The moment you switch to text based templates typesafety goes out of the window.

I really wish javascript/typescript had a good builder syntax like Ruby. Hyperstack [1] (previously Hyperloop) is a great project that uses ruby blocks for component composition. I wish this was possible in javascript.

[1] https://hyperstack.org/


I would advice against using JSX alternatives. I come from a Clojure background and I like the ijk solution to just use an array to represent the virtual dom.

But still I would recommend using JSX because it the "standard". Every documentation and problem solution you find on the internet is written with JSX. Using a JSX alternative will make debugging much harder for you.


The fact that front end has to deal with 3 different languages (JS + HTML + CSS) is a problem nobody has completely solved.

These days the solution usually falls into 3 camps:

1) Write everything in JS like React.createElement or Mithril. No build setup but a pain to use.

2) JSX. Requires a build setup which might become complex as the project grows.

3) Templates. Probably the less elegant of all 3 but the most pragmatic and easy to use.

Someone attempted to solve this and created a new language called Imba. It's like a mutant Ruby with HTML that compiles to JS.

http://imba.io/

It has a lot of productivity benefits compared to previous attempts at solving the same problem, but goddammit it's ugly.


The project "create-react-app" is a good mixture of #2 and #3 that I have been using for almost a year with almost no issues, it's just a generally great way to use React. The only issue I had is that, even though all your configuration is "for free", you don't really get new features right when they come out. But you get them after their configuration has been polished up, so that you don't have to shave all the yaks yourself.


Personally I'd rather have my own configuration than use a CLI like create-react-app or Vue CLI.

It's easy enough to create a template/starter kit the way you want it and tweak it to your taste. Also when (not if) you get a problem you know what's going on instead of a black box.


ClojureScript along with any of the various libraries for generating JS, HTML and CSS can get you to a single syntax. Granted, you still have to understand all those things on some level with the addition of learning ClojureScript and it's ecosystem.

For me, ClojureScript/Reagent [1] has been a dream come true. It marries JS, HTML and React very, very well. I still use normal CSS because I haven't hit a pain point with it yet, but there's some nice CSS tools that use EDN syntax and get rid of many CSS pain points. So far, I've only been able to use it for personal projects but they're not all small. I work with normal React at work on large projects and I've yet to run into anything I thought would be harder to do if were done with Reagent.

[1] https://reagent-project.github.io/


All those brackets and parentheses reminded me of the Lisp panel in the "Git the princess" comic.

https://toggl.com/programming-princess/

I will take a more serious look at ClojureScript, thanks for the suggestion.


I promise, they become practically invisible at some point :) And, usually if you count them compared to similar code in a c-like language, using data/markup from any popular format (xml, json, html), they'll be very close. Definitely far less when it comes to HTML vs. Hiccup.

I jump around between C#, JS, HTML, XML, JSON, JSX, CSS, SQL and Clojure all day long. I have to do the non-Clojure stuff a lot more and have been doing them decades longer, after just a few weeks of getting used to it, Clojure would be my first choice.


HTM actually looks pretty intriguing to me.

One thing I like about JSX is that it looks and feels like HTML.

For me, that has two huge benefits:

1. It narrows the dissonance between the code I'm writing and the actual structure that will be rendering into the DOM. I find it much easier to write and debug when I don't have translate between some JS factory function and the actual DOM implementation.

2. It's almost pure HTML. Sure I'll have to change class to className and update some other tags. Generally, though, if something works in HTML, it's little fuss to do in JSX. With tools like JSX, I can write in a single templating language across all of my software.


One thing the author missed about htm is that you don’t necessarily need to parse it at runtime— there’s a Babel plug-in to compile it to plain js function calls. This way you can develop without a transpiler and add one later when (if) the extra performance becomes necessary.


i'm surprised hiccup was never mentioned. ofc it's not for pure JS but there's this thing - https://github.com/lantiga/react.hiccup though i never tried and don't know how well it works. still interesting.


Hiccup is awesome for ClojureScript since we get to use plain Clojure data structures rather than a DSL that must be parsed in a separate step, but I think this advantage disappears when using it outside the context of Clojure/ClojureScript.


There's also kotlin-react which let's you write React in Kotlin in a Kotlin DSL which looks exactly like your first imagination probably suggests: https://github.com/JetBrains/kotlin-wrappers/tree/master/kot...

It's also a pretty impressive demonstration of the typesafe flexibility of Kotlin's DSL support, but that's another conversation.


ClojureScript + Reagent


This combo is insanely beautiful. Not only does it solve the JSX issue, it does it without any new syntax and adds persistent, immutable data structures, something React is just begging for. My only problem is that I'd have to switch jobs for any chance of using it at work.


I was thinking the same thing. Hiccup is a much better way to represent jsx than jsx.


The only one which is better than JSX imo.


A point to the discussion of whether JSX is preferable or not to a programmer, most React projects with JSX I've worked on has had the awesome bonus of a designer who knows markup/css to be able to add layouts to our web and React Native projects. Just wanted to throw that out there as a plus to JSX. I don't think the designer could follow hyperscript very well.


There's also HDOM, part of the larger Umbrella collection of libraries: https://github.com/thi-ng/umbrella/tree/master/packages/hdom

Purported benefits from the readme:

- Use the full expressiveness of ES6 / TypeScript to define user interfaces

- No enforced opinion about state handling, very flexible

- Clean, functional component composition & reuse, optionally w/ lazy evaluation

- No source pre-processing, transpiling or string interpolation

- Less verbose than HTML / JSX, resulting in smaller file sizes

- Supports arbitrary elements (incl. SVG), attributes and events in uniform, S-expression based syntax

- Supports branch-local custom update behaviors & arbitrary (e.g. non-DOM) target data structures to which tree diffs are applied to

- Component life cycle methods & behavior control attributes

- Suitable for server-side rendering and then "hydrating" listeners and components with life cycle methods on the client side

- Can use JSON for static components (or component templates)

- Optional dynamic user context injection (an arbitrary object/value passed to all component functions embedded in the tree)

- Default implementation supports CSS conversion from JS objects for style attribs (also see: @thi.ng/hiccup-css)

- Auto-expansion of embedded values / types which implement the IToHiccup or IDeref interfaces (e.g. atoms, cursors, derived views, streams etc.)

- Fast (see benchmark examples)

- Only ~6.2KB gzipped


Awesome article. I appreciate the lack of bias. Thanks for writing this, OP. It's good to keep an eye on the horizon for potential alternatives to tech we use day in day out, but it looks like we're still on solid ground with JSX so no need to switch :)


There is also a babel-transform for pug[1]. It works quite well as JSX replacement. Not having any endig tags can be quite beneficial when reformatting stuff.

Paired with Tachyons (best atomic CSS lib) you get superpowers and can write code like

  const someComponent = ()
    => pug`
      ul.p2.f3 Some list
        li.red Some text
        li.pink Some more`
[1] https://github.com/pugjs/babel-plugin-transform-react-pug


I think a lot of readablity issues with jsx are caused by the amount of logic react devs will put in their markup. It's painfully reminiscent of old-school PHP templating


My problem with JSX is that if you introduce a DSL, why would you limit it to basic function calls/expressions?

I created a small language to demonstrate what I mean https://github.com/batiste/blop-language


Easier to reason about.

JSX is a simple wrapper around a JS call, it is trivial to mentally convert from JSX to what the real JS is.

JSX is also super copy-pastable. Less logic and all that.

If you want complex logic around what JSX to render, it get factored out into a function call (or its own component), which returns the appropriate JSX after evaluating any required logic. This means you are back to plain JS with all the existing tooling and language features around it.

JSX being simple also makes the learning curve simple.


> JSX is a simple wrapper around a JS call, it is trivial to mentally convert from JSX to what the real JS is.

Then maybe it is an indication is not worth very much. Why not using something like hyperscript instead if it is just some javascript.

> JSX is also super copy-pastable. Less logic and all that.

In my experience the copy-paste-reuse in FE is a red herring. And React is no exception. And a logic less templating is not necessarily helping with a flexible/reusable piece of code.

> JSX being simple also makes the learning curve simple.

Not so sure about that. I think I would have personally preferred not having to learn a new DSL if some basic function call in Javascript can do the same job.

I guess I have been scared by colleagues using ugly ternary operator expressions with React.

I also used so many template languages in the past: PHP, Django, Pug, Rails, etc, etc. That I try to reproduce similar features/mistakes.


I was a bit apprehensive about JSX before quickly getting used to it. Now I love it.

The only decision I dislike is renaming class to className. Why not klass, cls, cs... anything shorter.

ClassName is simple to understand but adds a lot of clutter to component files.


Because it matches the field name for DOM nodes:

https://developer.mozilla.org/en-US/docs/Web/API/Element/cla...


lit-html surely does a good job..

- ES6 string literals

- partial template rendering

are few cool features with it.


lit-html is great!

- no build step

- ergonomic

- fast

- flexible

- useful whether alone or in a library/framework wrapper (see LitElement, https://lit-element.polymer-project.org/, Haunted, https://github.com/matthewp/haunted, et al.)

Getting just about all the convenience of JSX with the ability to have your templates run in pure JS in the browser, with no build step is pretty rad. Even if the difference is just a few seconds here or there, multiply that by the amount of work I'm supposed to be getting done in a day, and it starts to add up.

It's also nice to have control over whether to work with attributes or properties. A level of control that is mirrored by having direct access to event binding (rather than an abstraction of it) so you can pin down whatever custom events you might be passing around your application.

Maybe that's my favorite part, you get control. Whether via the time you get back, the say you have over your templates, the interoperability of your code, you have it with lit-html in a way that JSX hasn't really allowed me to in my experiences with it.


I haven't looked much into lit-html but is it possible to type check your templates like you can do with JSX? That is one big advantage I found for JSX compared to template-based frameworks.


All components, expressions, etc are in template curlies `${}` so type checking works out of the box. If you mean type checking on HTML properties, no it does not.

Lit HTML seems very extensible, so it should be possible to add support for thus.


It's possible. There's a VS Code extension that does this already called lit-plugin. If it becomes easier to add TypeScript compiler plugins via configuration, then I'm sure one will appear, allowing errors at build time.


In my UI library Laminar I offer this syntax:

    div(
      className("myClass"),
      "Hello, ",
      b("world")
    )
Basically a bunch of predefined element creation functions that accept variable argument lists of modifier objects. Very compact and ergonomic to type.

The library is implemented in Scala.js but I don't see why a similar API can't be done in Javascript just as well, I am not using any magic for this to work, no need for transpilation.

https://github.com/raquo/Laminar


If you read this article and thought Hyperscript might be something you wanted to try, please check out Mithril[0].

It's an excellent project, pretty decently fast (checkout the js-framework-benchmark[1] code, or more specifically the results of round 8[2]) -- but is currently suffering from a lack of recognition. It's the kind of project that absolutely doesn't care about that kind of metric (as in, the team seems to be more focused on slow, steady improvement of the library rather than chasing stars on github), but I figured I should say something.

Shameless plug: I recently really wanted to contribute something (and kick the tires more) so I wrote an article that goes through replicating a simple mail design I saw[3] -- it might be a decent overview of what mithril is like to write (though I'm certainly not a mithril expert).

This brings me back to the point at hand -- as others have noted, in my opinion one of the best things about hyperscript is the straight-forwardness with which you construct the render function. I'm not convinced it's necessary to segregate stateless/stateful components -- and mithril is simpler in that it doesn't introduce this dichotomy -- in the end there's the render function, and that's it. If you want to use state, go ahead -- if you don't, then don't. It's the simplicity I've wanted from component frameworks (and mostly get with Vue) without any of the posturing/looming complexity.

Also there's the fact that you could write a completely vanilla es5 application with hyperscript (arguments whether you should or not aside) -- JSX is/was revolutionary, but is basically required in practice, which often makes people jump into bed with webpack without thinking, and makes frontend development harder than it has to be.

[EDIT] - Another point for mithril is that it's self contained -- routing and ajax calls some with the framework. When people these days talk about "react" or "vue" what they're really talking about is react/vue + react-router/vue-router + flux/vuex and some other odds and ends. Mithril is by far the simplest and most feature-complete of these frameworks despite being smaller (both conceptually and on-the-wire).

One of my only current gripes with Mithril is the lack of controllable subtree rendering (it isn't as much of a problem in practice, but more me wanting to optimize early).

[0]: https://mithril.js.org

[1]: https://github.com/krausest/js-framework-benchmark

[2]: https://www.stefankrause.net/wp/?p=504

[3]: https://vadosware.io/post/mithril-systemjs-and-rollup-gettin...


I enjoyed your article very much. I'm a huge fan and proponent of this framework. Since I come across Mithril I have used it exclusively on all internal projects at our agency and haven't looked back.

Adopting Mithril also opened me up to a whole list of lesser known but vigorously thought through projects like BSS, Patchinko and the beautiful Meiosis pattern which changed the way I went about developing resulting in far better productivity.


Thank you very much! I really wanted to see more writing on the internet around Mithril -- it seems it's like lisp in the way that most people who are using it productively are just doing heads down working on stuff.

> Since I come across Mithril I have used it exclusively on all internal projects at our agency and haven't looked back.

I'd love to see some writing on some of the patterns you've developed with it! Also, it might be cool for mithril to have a "trusted by" section on their main page to showcase some companies that use mithril to increase trust for people who wander in (though I'm also OK with mithril not having this section -- it's a bit overly-prodcuty and distracting).

> Adopting Mithril also opened me up to a whole list of lesser known but vigorously thought through projects like BSS, Patchinko and the beautiful Meiosis pattern which changed the way I went about developing resulting in far better productivity.

> Adopting Mithril also opened me up to a whole list of lesser known but vigorously thought through projects like BSS, Patchinko and the beautiful Meiosis pattern which changed the way I went about developing resulting in far better productivity.

Thanks a ton for the references, looking up all those things right now, looks like more pieces of the Mithril ecosystem that I've never heard of. Patchinko looks super useful (I'm surprised I've never needed something like this in the past...), and Meiosis looks like a simpler version of the reactive/flux pattern, and looks interesting to me since it's cross-framework!


So most of these projects are all maintained by members of the Mithril team or heavy contributors of Mithril, so you can trust they are created with care and have been thoroughly thought through.

You should definitely join the Gitter chat for Mithril. It's a very active channel and a little lesser known part of the internet where you will get a constant flow of engaged developers of all levels discussing Mithril and also where some of the core team discuss concepts, roadmaps, ideas and drop knowledge.


Mithril doesn't yet support ES Modules, so you have to take in the whole framework which probably includes things you don't want, like their ajax alternative or promise polyfill.

If you just want a vdom library, I would recommend something like https://github.com/snabbdom/snabbdom


That's not quite true. While it's true that it doesn't export treeshakeable exports, you can import things piecemeal like `import h from 'mithril/hyperscript'`.

The rationale is that mithril always tries to be explicit, whereas treeshaking is a form of compiler magic.


Context: I'm pretty sure the parent (to this comment), lhorie, is the creator of Mithril:

https://github.com/MithrilJS/mithril.js/graphs/contributors

Thanks to you and the team of contributors for your continued hard work on Mithril! I'm having a blast using it.


I am getting a 404.


Works for me.




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: