Skip to content

Instantly share code, notes, and snippets.

@sebmarkbage
Last active May 16, 2022 13:48
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sebmarkbage/f1f4ba40816e7d7848ad to your computer and use it in GitHub Desktop.
Save sebmarkbage/f1f4ba40816e7d7848ad to your computer and use it in GitHub Desktop.
React JSX - Lower Case

Lower Case JSX Convention

All lower case JSX tags will now be treated as HTML/SVG elements. They will no longer be treated as custom components in scope.

The React element produced by JSX can be either a React class that exists in the local scope or a global scope HTML/SVG element depending on a convention.

Previous Behavior

Currently, when you use React JSX to define a HTML element you can use any known HTML tag. E.g:

return <div />;

This means that we never use the local variable for HTML tags:

var div = React.createClass();
return <div />; // HTML tag, not the custom class

If you use a non-HTML tag we DO use the local variable:

var component = React.createClass();
return <component />; // custom component

The Whitelist Problem

We have a whitelist of HTML tags. This becomes a problem because nobody knows the full list of HTML components. It's also not a static list.

This means that every time there's a new HTML/SVG tag, you can't use it until we add it to the whitelist. You can't add it to the whitelist until you update your existing codebase. For example, we recently added the picture tag. Without a codemod something like this would've broken:

var picture = React.createClass();
return <picture />;

Nobody wants to maintain this whitelist and update their codebase every time it changes.

New Behavior

The new convention is that any JSX identifers that starts with a lower-case characters are assumed to be HTML tag:

var component = React.createClass();
return <component />; // HTML tag, no longer a custom component

This also works with lower camel case:

return <foreignObject />; // SVG tag, not custom component

To access a local variable you'll have to use an upper-case convention:

var Component = React.createClass();
return <Component />; // custom component

This can be inconvenient if your style guide requires you to name classes with lower-case names.

Plausible Alternative 1: Explicit Syntax

We could add some explicit syntax to differentiate between HTML tags and local variables. E.g. we could add string syntax around HTML tags:

return <"div">content</"div">;

However, this no longer looks like HTML which is one of the nice features of JSX. It's quite annoying if you're creating HTML heavy components.

Another alternative would be to wrap custom components in explicit syntax:

return <{Component}>content</{Component}>;

Unfortunately, it is best practice to use a lot of custom components with React. Everything is a component. This starts becoming inconvenient and it's one of the reasons we have JSX instead of just template literals.

The inconvenience seems to be a deal breaker, at least for now.

Plausible Alternative 2: Scope Chain

It's possible that we could check for variables in the currect scope chain (assuming strict mode restrictions of with and eval).

If a local variable is in scope, then we'd use it. Otherwise, we'd assume that HTML is used.

function a() {
  return <picture />; // HTML tag
}

function b() {
  var picture = React.createClass();
  return <picture />; // custom class
}

This could potentially be a refactoring hazard if you accidentally remove a variable and now your JSX starts outputting strange HTML tags.

Additionally, there is some confusing behavior when common variable names overlap with picture elements:

function getListItems() {
  var list = [];
  for (var i = 0; i < 10; i++) {
    // This would end up trying to use the local variable `i`
    // as a custom component:
    list.push(<li><i>Italic Text</i></li>);
  }
  return list;
}

A surprising number of people still use applications with global scope or custom module systems. It would not be possible to use components that exist outside of the local scope chain that our transformer can analyze.

Since this is a tooling nightmare right now, we don't consider this a viable alternative at the moment. It might be possible in the future when build and module systems are better standardized and widespread.

@koistya
Copy link

koistya commented Oct 15, 2014

Perhaps yet another alternative would be to use namespaces for all custom components:

<picture /> // HTML tag
<app:picture /> // A custom component

@masbicudo
Copy link

Because no alternative is refactoring free, I'd vote for none of these alternatives. Just force everybody to follow the convention (first letter upper case), it's one letter refactoring for components using lower-case... which I think is rare, by the way.

The scope chain was the best alternative, until I read the <i> sample... that's the worst case conflict, and for me it's an alternative killer... bang! Nothing remained, so... one letter refactoring looks very cheap on the long run.

@mik01aj
Copy link

mik01aj commented Sep 2, 2015

+1.

If there is anyone protesting against the upper/lowercase convention, you can always add the explicit syntax so they can write <{lowercaseComponent} /> and <"UppercaseTag" />.

@rramteerath
Copy link

The alternatives seem fragile and a debugging nightmare. +1 for the uppercase convention.

@ajfarkas
Copy link

ajfarkas commented Feb 4, 2016

I'm all on board for this, but I still get a compile error when I use a <use> tag. I'm using it as an HTML tag, not a React class.

So it looks like there's a whitelist somewhere after all?

@uatec
Copy link

uatec commented Mar 28, 2016

Yeah. This doesn't actually happen. You get an error linking to this page. e.g. when writing SVG: tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment