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.
Perhaps yet another alternative would be to use namespaces for all custom components: