DEV Community

Cover image for Introducing Literal JSX
Kelley van Evert
Kelley van Evert

Posted on

Introducing Literal JSX

Very brief introduction: (1) it looks like this:

…(2) and that's all there is to it!

But that's just JSX!—you might say. And you're right, it is indeed JSX (spec / React docs), which I didn't invent. Rather, I decided to specifically subset the JSX syntax—which is itself an extension of the JavaScript expression—to its sensible and simple data-structural part, in a manner akin to what JSON is to the JavaScript expression. In a diagram:

The arrows signify syntax extensions, or their reverse: syntax subsets.

So why subset JSX syntax?—you might ask.

Let's take a step back for a moment, and I'll lead you to the same "invention" of Literal JSX as I had.

JSX was invented alongside, and popularized in the context of Facebook's React, the library that prioritized declarative components as the main building block for building comple UIs (and the rest is history). The main technological invention that fuelled this component-based approach was the idea of Virtual DOM as a functional and reactive API to wrap the stateful DOM API. But JSX doesn't really have anything to do with the latter. In order to write components declaratively using virtual DOM, we could have kept to just writing javascript that might look like this (and sometimes we still do):

function Greeting({ name }) {
  return { nodeType: "div", children: [
    { nodeType: "span", text: "Hello ," },
    { nodeType: "span", text: name }
  ] };
}

Thesis: JSX is about ergonomics and intent

The comparative advantage that JSX has over just datastructures (or data structure creating function invocations) can best described if boiled down to the comparative advantage that JSX would have over just JSON—JSON being the de-facto data exchange format in JavaScript world. In other words, choosing to rather write

  • <Greeting name="Kelley" />

than for example

  • { "component": "Greeting", "attrs": { "name": "Kelley" } }

I can imagine two reasons we prefer the former over the latter:

  • Ergonomics.

    Obviously, because it comprises of less characters, and programmers just love to reduce the number of characters they need to write. (Especially in the web community, which revolves around a dynamic language that historically has allowed all kinds of creative overhead-reducing inventions.)

  • Intent.

    But I think there's more to it. I think the "elevation" of the string Greeting from just the value of a property (here "component") to the name of the thing might be the most important distinction. It looks like HTML (or XML), where the name of the thing defines the layout, semantics, etc.

    There is a specific, intended semantics (presentational, interactive, etc.)—as opposed to it "just being data".

Se let me annotate the diagram above with these insights:

Thesis: JSON was an important invention as a standard

By subsetting the JavaScript syntax to the bare minimum of data structures (and imposing some formatting contraints), Douglas Crockford created a stupid simple, obvious data exchange format. You could say it "won" the battle with XML as a lightweight data exchange format for the web. So what does JSON have that made it popular?

  • It has ergonomic benefits over XML. If you want to easily exchange data, you don't want to have to make too much decisions, and typically transforming data structures to XML involves making many such decisions (often relating to semantics such as the question what is type, what is attribute, etc.) Also, you need less characters.
  • It has an unambiguous single-pass parseable grammar, which makes it really easy to implement, and fast to use as well.
  • It's a standard instead of an implementation. This point cannot be stressed enough.

Final thesis: Literal JSX can be a sensible standard for exchanging data with nominal UI-structure

There are two main use-cases I can think of when it comes to exchanging "UI data"

Use case: content authoring

It's a rage to author content in Markdown (and other related lightweight markup formats). Due to the principle of "less structure makes for easier interopability" (also related to the idea of "garbage in, garbage out"), and together with its visual simplicity, Markdown seems a near-perfect compromise between content authoring and a markup format. However, frequently we want to break out of the simplicity of Markdown, to add domain-specific extra's.

For example, dev.to itself has a whole slew of "additional components" you can place in your article (with Liguid tags), such as integrations with Codesandbox, Glitch, GitHub, and some dev.to specific addons, etc. Many alternatives exist out there to add such additional components to Markdown text, but they're all based on what I call "grammar hacks": just a cleverly chosen grammatical "escaping identifier" such as {% ... %} or [[ ... ]] or $$ ... $$ or %% ... or ..., but no rigorous additional grammatical specification, rendering it too specific and inflexible.

At Codaisseur (where I teach full-stack dev), we too author our course materials in Markdown, version control them, etc. We have small exercises strewn through our course materials, as well as some interactive code editing integrations. But we're aiming for a more general solution of adding more types of interactivity and exercises to our contents, without sacrificing the ease and flexibility of Markdown authoring, keeping our contents version controlled, etc. In short, Literal JSX gives us the possibility to write Markdown-based materials like the following:

---
title: "JavaScript 6 - Scoping"
learning_goals:
  - id: var_shadow
    description: "Understand variable shadowing"
  - id: analyze_scope
    description: "Analyze where a variable is \"visible\""
  # etc
---

## Welcome to the JS 6 module!

<LearningGoals showProgress />

<TeacherContent>
- Remember to discuss how block statements introduce new scopes!
- And show them the example of creating functions inside a for-loop
</TeacherContent>

[...]

## Exercises

<VariableShadowingExercise
  learning_goal="analyze_scope"
  variables={["name", "age", "totalAge"]}>
let totalAge = 0;
const people = ["Aretha", "Avishai", "Alice"];
for (const name of people) {
  const name = person;
  const age = Math.floor(Math.random() * 50);
  totalAge += age;
}
</VariableShadowingExercise>

But MDX already exists, why do we need Literal JSX for this?—you might ask. And you're right, this is almost exactly the problem MDX is solving. But with one crucial difference: MDX is an implementation, where Literal JSX hopes to be a standard. Concretely though, this means that it is infeasible to render our contents in MDX, because it would either require having the bundler (say Webpack or Parcel) read all of our course materials (a lot) at build-time, or else we'd have to have the MDX runtime (which includes a full-fledged JavaScript expression parser and transpiler) in the application. And it wouldn't make sense either: because content is data, not code.

Use case: actual programmatic exchange of UI data

Because this would make more flexible, the traditional split between (on the one hand) the application, which needs to understand how to visually structure data; and (on the other hand) the content, which then needs to follow the structure defined beforehand by what is presentable in the application.

Literal JSX, defined

So there we have it then. Literal JSX, to my mind, is a general, but more importantly, obvious solution to the problem of exchanging UI content. JSX already is the most widely adopted and recognizable face for authoring "UI components". All we did is to strip it from the full JavaScript language spec, and subset it to just include well-founded data structures, and with an easy single-pass parseable grammar.

In lovely railroad grammar diagrams, this is what it looks like. First, the simple grammar for a trimmed-down JSON-like version of JSX:

And, then, we extend the syntax of JSON to include the "Element" non-terminal:

The result? The following are now valid Literal JSX values (i.e. valid syntax for the "Value" non-terminal):

  • 42
  • "Just a String"
  • <SomeComponent />
  • ["a string", <AComponent />, 3.14]
  • <Button data={["a string", <AComponent />, 3.14]} />
  • etc.

So what now?

  • Head over to the website, to look at those lovely railroad diagrams :)
  • Check out my example implementation on npm, and use it for something cool!
  • Write a better implementation, in your language of choice, for your language of choice!
  • Let me know whether you think what I'm saying is sensible! :D

Discussion (0)