Skip to content

Boolean attributes #1101

@edemaine

Description

@edemaine

Questions: How should JSX attributes foo and foo={true} behave for an HTML element, as in <div foo> or <div foo={true}>? Should the behavior differ for components and/or spreads?

Current behavior in Solid:
Assume const Div = (props) => <div {...props}/>

JSX Equivalent HTML
<div foo> <div foo="">
<div foo=""> <div foo="">
<div foo={true}> <div foo="true">
<div foo="true"> <div foo="true">
<Div foo> <div foo="true">
<Div foo=""> <div foo="">
<Div foo={true}> <div foo="true">
<Div foo="true"> <div foo="true">

The ☆ behavior (another consequence of setAttribute's string casting) is particularly counterintuitive, as it differs between HTML elements and component-wrapped elements. (It may also be an SSR discrepancy?) @fabiospampinato's observation of this weirdness is what spawned this exploration (in #templating channel, also with @lxsmnsyc).

Relevant facts:

  • HTML 5 defines attribute foo to be equivalent to foo=""
  • React defines that foo is equivalent to foo={true}.
  • TypeScript treats foo like foo={true} (attribute should accept Boolean value)
  • React (specifically react-dom) converts JSX attribute foo to HTML equivalent of foo="true" unless foo is on a whitelist of "Boolean" attributes, in which case it converts to foo="". Here's a relevant list; note that there are some Boolean-like attributes which aren't actually Boolean. For example, React "correctly" converts draggable={true} to draggable="true" and "correctly" converts async={true} to async="".

Desired properties:

  1. foo is always equivalent to foo="" for HTML/SVG elements (as in HTML 5). This is useful for copy/pastability of HTML, which the JSX spec considers important.
  2. foo is equivalent to foo={true} (as in React and TypeScript)
  3. draggable={true} is equivalent to draggable="true"
  4. <div foo> is equivalent to const Div = (props) => <div {...props}/>; <Div foo>

We can't have all four properties.

  • Properties 1, 2, and 3 contradict each other: 1+2 imply draggable={true} is equivalent to draggable="".
  • React satisfies Properties 2, 3, and 4. It follows Property 1 only for a whitelist of Boolean attributes.
  • Solid satisfies Properties 1 and 2. It satisfies Property 3 for Div but not div. It doesn't satisfy Property 4 because of ☆.

Proposal:
I really like Property 1, and would propose gaining Property 4 by compiling foo={value} to essentially _el$.setAttribute('foo', value === true ? '' : value), where value is stored in a temporary variable if it's a general expression, with a similar modification to spreads. This would basically flip ☆, and also change those marked "!":

JSX Equivalent HTML
<div foo> <div foo="">
<div foo=""> <div foo="">
<div foo={true}> <div foo=""> !
<div foo="true"> <div foo="true">
<Div foo> <div foo="">
<Div foo=""> <div foo="">
<Div foo={true}> <div foo=""> !
<Div foo="true"> <div foo="true">

(By contrast, React's approach of an attribute whitelist feels gross...)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions