Super Mario, JSX, and the destruction of the web development learning curve
Tags: webdev, react, marko, beginners
Published: 2023-01-25 14:58:16

_Super Mario Bros_ is a triumph of accessibility: in the 1980s, millions of people new to video games booted up the NES and immediately learned how to run, jump, dodge enemies, break bricks, and collect coins. A focus on approachability was a keystone of its design, and was carried over its many sequels. Luckily, the design strategies used in Mario games are no secret.

Nintendo's family-friendly empire [is built with a pattern they've stuck to for almost 40 years](https://www.nyfa.edu/student-resources/nintendo-can-teach-us-game-design/). Players begin in a safe and friendly environment, free to explore the game mechanics and controls. After this introduction, new concepts are introduced one-by-one and challenges are presented that let players demonstrate what they've learned.

With the standard Web languages of HTML, CSS, and JavaScript, learning to make websites feels a lot like playing a Nintendo game. HTML empowers its authors to publish webpages of all shapes and sizes, while [its low barrier to entry](https://codepen.io/tigt/post/low-barrier-to-entry-the-most-powerful-characteristic-of-the-web) makes it available to people of all experience levels. Designing simple websites is easy and exploratory, and [complex behavior is additive](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement).

## The Approachability of HTML

When Mario becomes Fire Mario, his existing controls don’t change. Instead, he throws a bouncy flame _in addition_ to running when the player presses <kbd>B</kbd>. Adding CSS to HTML is similar; nothing about writing HTML changes (except probably more `class` attributes), but the site's style is enhanced. Just as Mario doesn’t need to know how to triple jump or wall kick before tossing fireballs, a developer doesn’t need to know the details of form submission before aligning elements with flexbox; even though CSS layers on top of HTML, the two can be learned in parallel.

![HTML in Mario’s costume, holding CSS the Fire Flower and JavaScript the Super Mushroom.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o346fdf1kph901hyjse3.png)

Traditionally, JavaScript was similar to CSS. It let developers enhance sites with interactivity by dynamically updating HTML and CSS, without first requiring mastery of them. As such, the web development learning curve was smooth and approachable.

![An non-scientific graph with Complexity on the Y axis and Time on the X axis. A sketchy line starts from the bottom-left and wanders to the upper-right, showing a steady progression in complexity from HTML to CSS to JavaScript.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/amd9103rypv3gitbl3i9.png)

However, many web engineers assert that vanilla HTML, CSS, and JavaScript are insufficient for large projects, especially when sites heavily manipulate the DOM. Even for a basic client-side shopping list, _most_ code is likely to be written in JavaScript, [which is harder to understand and maintain than HTML](https://blog.codinghorror.com/the-principle-of-least-power/).  Here's one example of how a shopping list app may be written:

{% codepen https://codepen.io/lukelavalva/pen/rNrWbaN default-tab=html,result %}

To understand the code above, developers are faced with the concepts of `querySelector`, template cloning, and hardcoded element manipulation. Information is fragmented, and multiple places must be touched to make changes.

Those maintainability complications are especially apparent when many developers work on the same codebase, or frequently switch teams and projects. Since JavaScript has no “best” way to manipulate the DOM, vanilla JS projects tend to have different sets of opinionated code. Libraries like jQuery (which is still [widely used](https://trends.builtwith.com/javascript/jQuery)) weren't the complete solution that developers were looking for. This may be why JavaScript frameworks have overtaken traditional web development in recent years. Here's an equivalent shopping list in React, the leading JavaScript framework:

{% codepen https://codepen.io/lukelavalva/pen/JjBbzVG default-tab=js,result %}

This React example has more code than its vanilla equivalent, but the logic is presented with a consistency that appeals to experienced software engineers. This consistency makes it easier to switch across React codebases than switching between vanilla projects. Also, [separating UI into components offers benefits](https://atomicdesign.bradfrost.com/chapter-2/#advantages-of-atomic-design) like letting engineers work in parallel without conflicts (at least [in theory](https://weareadjacent.com/blog/page-driven-vs-component-driven-design/)).

This shopping list is a _simple_ example of an app with DOM manipulation. As an application grows, it becomes harder to argue that the vanilla solution is more maintainable or easier to understand. This is why common advice for aspiring web engineers is to "learn React".

Unfortunately, beginners are [quickly faced](https://bradfrost.com/blog/post/my-struggle-to-learn-react/) with a large front-loading of complexity inherent to a JSX project.

## The Approachability of React

[React’s official documentation](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) instructs newcomers to begin by downloading VSCode, installing Node.js, and navigating a terminal to type `npx create-react-app my-app`, after which they encounter this mound of files:

![The file tree in a create-react-app folder named “my-app”. There’s a “node_modules” folder, a “public” folder containing favicon.ico, index.html, logo192.png, logo512.png, manifest.json, and robots.txt; a “src” folder containing App.css, App.js, App.test.js, index.css, index.js, logo.svg, reportWebVitals.js, and setupTests.js; and at the root are .gitignore, package-lock.json, package.json, and README.md.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sjekzy8urlk5a17b5t6.png)

To create their "Hello World", beginners are told to find the `src/App.js` file and replace [its lightly-confusing contents](https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/cra-template/template/src/App.js) with:

```jsx
import React from "react";

function App() {
  return (
    <p>Hello World</p>
  );
}

export default App;
```


Compare _all of that_ to opening a text editor and typing some explainable HTML, which [`create-react-app` _also_ introduces](https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/cra-template/template/public/index.html):

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <p>Hello World</p>
  </body>
</html>
```

With React, the difficulty curve becomes a difficulty cliff. To fit with the analogy, learning web development in React is like starting Mario in this level:

![Tanooki Mario in a desert, surrounded by fireballs, Piranha Plants, Munchers, Swinging Claws, a Blooper, a Spiny, a Cheep-Cheep, and a giant Goomba.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmlievmym736i4roa8ow.png)

To demonstrate this front-loading of complexity, compare this HTML+JS counter and its React equivalent:

```html
<!DOCTYPE html>
<html>
  <head><title>Counter</title></head>
  <body>
    <button id="counter" onclick="increment()">0</button>
    <script>
      let count = 0;
      function increment() {
        count = count + 1;
        counter.innerHTML = count;
      }
    </script>
  </body>
</html>
```

```jsx
import React, { useState } from "react";

function App(props) {
  const [count, setCount] = useState(0);
  const increment = () => {
    setCount((curr) => curr + 1);
  }
  return (
    <button onClick={increment}>{count}</button>
  );
}

export default App;
```

For the vanilla example, a new developer must learn

- General page and tag structure
- Buttons and the `onclick` attribute
- The `<script>` tag
- Variables with `const` and `let`
- Functions and function calls

_In addition_ to those, the React example requires knowledge of

- ES6 `import`/`export`
- Object destructing
- Anonymous/arrow functions
- Functional programming concepts (can't set `count` directly)
- JSX templating

For beginners who often [already struggle with basics like variables](https://medium.com/groklearning/top-ways-students-struggle-to-code-with-variables-933ef4026766), React raises further barriers to entry. For `useState` alone, a new programmer has know

- [The rules of hooks](https://reactjs.org/docs/hooks-rules.html)
- That functions can be stored in variables
- That even though `count` is a `const`, its value updates with `setCount`
    - However, if they reference `count` in the same function they call `setCount`, they'll find that its value _did not change_.

That’s all routine for seasoned React developers, but it's a _lot_ for beginners to take in.

## The Future of Web Frameworks

Despite its learning curve, React was a hit because it addressed problems that plagued web developers in the mid-2010s. Many companies were experimenting with client-side interactivity, but their attempts with vanilla JavaScript scaled poorly as teams grew and more state management was required. React seemed like a perfect solution, and developers used it to build _millions_ of sites. However, other options have appeared, and some developers are growing disillusioned with React and JSX.

The problem highlighted in this post is that JSX doesn't _build on top_ of the vanilla experience as much as it _replaces_ it, while still requiring knowledge of HTML and JavaScript. As a result, the learning curve for React and its successors, like Solid and Qwik, is steeper than HTML+CSS+JS.

My criticism of React and JSX isn't to disregard the value that frameworks add. Instead, it's an aspiration for creators of future web technologies. **Along with improving developer experience for large projects, frameworks should preserve the web development learning curve.** I hope that the next era of web development will be built with tools that encourage hobbyists and experienced developers alike.

Frameworks like Astro and Svelte are fairly effective at preserving web development’s learning curve, but my money is (quite literally) on [Marko 6’s new Tags API](https://dev.to/ryansolid/introducing-the-marko-tags-api-preview-37o4). In addition to all of its performance benefits, Marko's syntax brings the modern developer experience much closer to that of vanilla HTML. To me, adding Marko to the vanilla web languages feels a whole lot like collecting a power-up.

![Animated pixel art of angle brackets surrounding a slash powering up to the Marko logo as if it were Mario](https://piskel-imgstore-b.appspot.com/img/a0573668-9c59-11ed-b281-ad6cd3bb0c00.gif)

### Appendix: examples rewritten with Marko

- [Hello World](https://markojs.com/playground/v6/#NobwRAdghgtgpmAXGAlhAJnAHgOhlAJwGsB7MAGjAAcoAXACyTAHoBjEmKkiOCWgZ2ZpMufMTKV2fXrSYAeKgD4AEnAA2akgAIA6iQJr0c5krABfALpA)
    ```html 
    <p>Hello world</p>
    ```

- [Basic Counter](https://markojs.com/playground/v6/#NobwRAdghgtgpmAXGAlhAJnAHgOhlAJwGsB7MAGjAAcoAXACyTAHoBjEmKkiOCWgZ2ZpMufMTKV2fXrSYAeADZxabEgFc+AAgC8mgAzMAfAB0IcgEZratbpu4BhBSlZEAFAEpNITew20A1P6aAL4mEJqaACQgvnzBpnLMltbchmDBALpAA)
    ```html 
    <let/count = 0/>
    <button onClick() { count++ }>
      ${count}
    </button>
    ```

- [Shopping List](https://markojs.com/playground/v6/#NobwRAdghgtgpmAXGAlhAJnAHgOhlAJwGsB7MAGjAAcoAXACyTAHoBjEmKkiOCWgZ2ZpMufMTKV2fXrSYAeADZxazBSn60ABAF5NwALrMAfAB0IZuQDMSBGJu4BlAK4AjGCloAKOAEpNIM01NKQ1NNConLV0eLFoASVo4GE8fAG5AzTVQ3SzaHClWOk9wyJwANygFJ190iCCSvIqquB1NEzB22qC4HCoCODKZABE4SygnBS80swBfUzrNRSgXOAUjADk4AHcwxJhERYbmGPi9ozlVZdX5oLkXSNpuIwBBdHRNR8z1Wgv72keIPMLtZbPMLBMbosQQAfDxJci7JJxDDYaH2SzaXKQ25qIwAEhAcJgMzaCyCtz+APsEAAwmpWEQUv4MuTyblWrkcJYUJM4ARPJ4APoIlB+bRGMKaACE2l0RORIh8LPJc2VrJGSkSasWzEpT2VF1xGWBNiBzAhYBm+iAA)
    ```html 
    <let/list = []/>

    <form onSubmit(e) {
      const input = nextItem();
      list = list.concat(input.value);
      input.value = "";
      e.preventDefault();
    }>
      <label>New item: <input/nextItem></label>
      <button>Add to list</button>
    </form>

    <ul>
      <for|item, itemIndex| of=list>
        <li>${item}
          <button onClick() {
            list = list.filter((_, i) => i !== itemIndex)
          }>
            Delete
          </button>
        </li>
      </for>
    </ul>
    ```