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. 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 makes it available to people of all experience levels. Designing simple websites is easy and exploratory, and complex behavior is additive.
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 B. 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.
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.
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. Here's one example of how a shopping list app may be written:
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) 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:
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 like letting engineers work in parallel without conflicts (at least in theory).
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 with a large front-loading of complexity inherent to a JSX project.
The Approachability of React
React’s official documentation 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:
To create their "Hello World", beginners are told to find the src/App.js
file and replace its lightly-confusing contents with:
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:
<!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:
To demonstrate this front-loading of complexity, compare this HTML+JS counter and its React equivalent:
<!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>
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
andlet
- 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, React raises further barriers to entry. For useState
alone, a new programmer has know
- The rules of hooks
- That functions can be stored in variables
- That even though
count
is aconst
, its value updates withsetCount
- However, if they reference
count
in the same function they callsetCount
, they'll find that its value did not change.
- However, if they reference
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. 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.
Appendix: examples rewritten with Marko
-
<p>Hello world</p>
-
<let/count = 0/> <button onClick() { count++ }> ${count} </button>
-
<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>
Top comments (9)
I tend to think the pitfalls of immutable data tied to framework re-renders were an outsize barrier to entry for junior (and let’s face it, a lot of senior) devs, since mutating data is usually easier in commonly-taught programming languages. But with that link to how normal variables are tricky for beginners to grasp, I wonder if there’s a way to make immutable data handling more newbie-friendly than what we currently have?
I've thought about this a lot, mutable reactive data seems to be a hurdle that no framework has been able to fully overcome yet (correct me if I'm wrong, I'd love to hear about a solution).
Polymer 0.5 did it using Array/Object observers. It was plain simplicity and I fckin loved it. Than it was removed from JS spec due to performance concerns, which I disagree with. I was using it with a more than 5k item array with deeply nested objects (an app used in production). At that point I started to feel degraded performance indeed, but there was still room for optimizations, and it's not a common scenario for a lot of cases. Somehow the industry tends to sacrifice simplicity for performance reasons.
Yeah, JavaScript seems to be leaning pretty heavily into functional programming for arrays and objects instead of increasing support for mutability. I don't think it will be that hard for people to get used to replacing
arr.push(newItem)
witharr = arr.concat(newItem)
, but nested objects are a little bit more tricky to work with.In addition, libraries like Immer are making immutable state easier to work with. This helps a little bit with the learning curve for React, but what I find interesting is that it wouldn't be out of the question for compiled languages like Marko to provide this behavior out of the box.
So, Marko is the next attempt to bring some programming capabilities to HTML? The question is, how far this will take us, reinventing the wheel again and again?
This will mean:
a) introduce a new programming paradigm into HTML (which in fact is no programming language)
b) Intoduce a whole new syntax and a full set of keywords to make it usabe.
I´m not sure this will flatten the learning curve in the end.
A lot of effort has been put into keeping Marko as close to HTML and JavaScript as possible (as if they were designed together), so my hope is that there isn't too much more to learn in order to start using Marko in a web project. My opinion is that the learning curve for Marko is flatter than that of leading frameworks like React and Angular, but that is up for debate.
Regardless of whether Marko takes off, many companies and developers have decided that the vanilla languages are insufficient as scalable solutions for large teams. My hope is that as the industry brings in frameworks, the tools they choose are ones that are friendly for beginners.
Adding an <if> tag does not make HTML a programming languange. Sooner or later people will ask, how to write nested loops or simply count from one to ten. Then you end up like React, where this most natural question will be answered with most complicated solutions.
I did not know Marko before, but if my first impression is correct, Marko uses a whole bunch of "pseudo" commands like or with a very unique syntax. It uses JS classes, so I suppose, inside a class, the full JS syntax is valid. So you end up with a language with two different "if" and "for"-elements (one from JS and one from Marko). Does not sound "simple" to me...
This is a very good point, it would be great if there were only one way of accomplishing each task. Marko's promise to preserve all JavaScript functionality while allowing for an HTML-esque authoring experience has sort of backed it into a corner where it needs to have its own version of the for loop. My biases tell me that because Marko's
<for>
tag is so similar to JavaScript'sfor
loop this isn't a huge source of concern, but I am certainly interested in testing my hypothesis.Love this extended Mario metaphor.