Jump to content

JavaScript XML

From Wikipedia, the free encyclopedia
(Redirected from JSX (JavaScript))
JavaScript XML
Paradigmdomain-specific language, language extension
FamilyECMAScript, XML
Designed byFacebook
DeveloperFacebook
First appearedAugust 29, 2014; 11 years ago (2014-08-29)[1]
Typing disciplineweak, dynamic
Filename extensions.jsx, .tsx
Websitegithub.com/facebook/jsx
Major implementations
Babel
Dialects
React

JSX (sometimes referred to as JavaScript XML) is an XML-like extension to the JavaScript language syntax.[2] Initially created by Facebook for use with React, JSX has been adopted by multiple web frameworks.[3]: 5 [4]: 11  Being syntactic sugar, JSX is generally transpiled into nested JavaScript function calls structurally similar to the original JSX.

When used with TypeScript, the file extension is .tsx.[5]

Process

[edit]

Code written in JSX requires conversion with a tool such as Babel before it can be understood by web browsers.[6][7]: 5  This processing is generally performed during a software build process before the application is deployed.

The transpiler (such as Babel) must make assumptions about the nature of the JSX use, otherwise it would not know what to convert JSX tags into. In the case of Babel (below), we use it in a mode which assumes that the tags are to be used with react, so it converts JSX tags into function calls to the React JSX Runtime, which return values corresponding to the internal representation of these tags.[8] @babel/plugin-transform-react-jsx, which Babel uses for JSX, provides some flexibility for adapting to other platforms by allowing specifying alternative pragmas that take the role of React.createElement and React.Fragment.[8]

Markup

[edit]

The following examples include both the React-JSX/TSX code and the transpiled output from Babel.

Basic example

[edit]
TSX Code Transpiled output (Babel)
import React from 'react';

const App: React.FC = () => {
    return (
        <div>
            <p>Header</p>
            <p>Content</p>
            <p>Footer</p>
        </div>
    ); 
}

export default App;
import React from 'react';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const App = () => {
  return /*#__PURE__*/_jsxs("div", {
    children: [/*#__PURE__*/_jsx("p", {
      children: "Header"
    }),/*#__PURE__*/_jsx("p", {
      children: "Content"
    }), /*#__PURE__*/_jsx("p", {
      children: "Footer"
    })]
  });
};
export default App;

The /*#__PURE__*/ comment added by Babel indicates that the tagged call is to a pure function, which is relevant for later steps such as tree shaking.[9] Because it is not necessary for the functioning of the code, following examples have this annotation removed for ease of reading.

The above transpiler output indicates an output from React 17+. In older versions the output would instead be a more straightforward:

const App = () => {
  return React.createElement("div", null, 
    React.createElement("p", null, "Header"),
    React.createElement("p", null, "Content"), 
    React.createElement("p", null, "Footer"));
};
export default App;

The classic interface remains commonly used by non-React users of JSX.

Nested elements

[edit]

Multiple elements on the same level need to be wrapped in a single element such as the <div> element shown above, a fragment delineated by <Fragment> or in its shorthand form <>, or returned as an array.[10][11][4]: 68–69 

Attributes

[edit]

JSX provides a range of element attributes designed to mirror those provided by HTML. Custom attributes can also be passed to the component.[12] All attributes will be received by the component as props (properties).

Valid JSX attribute names are valid JSX identifiers (variable names), which differ from ECMAScript identifiers mainly by additionally allowing the - (hyphen-minus) character in a non-starting position. This allows HTML attributes with dashes such as data-* and aria-role to be used easily. Attributes still with invalid characters may be notated with the {... spread} syntax.[2]

TSX Code Transpiled output (Babel)
<a
  href="https://en.wikipedia.org"
  data-note="Wikipedia"
  {...{ ":tricky-property-name": a }} // XML property may contain and start with colon
>
  Wikipedia
</a>;
React.createElement(
  "a",
  {
    href: "https://en.wikipedia.org",
    "data-note": "Wikipedia",
    ":tricky-property-name": a,
  },
  "Wikipedia",
);

Custom components

[edit]

Custom "tags" (components) are simply JavaScript functions that accept a specific calling style.

JSX Code Transpiled output (Babel)
class Hello extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

function Jello({ name }) {
  return <h1>Jello, {name}!</h1>;
}

export default function App() {
  return (
    <>
      <Hello name="Sarah" />
      <Jello name="Connor" />
    </>
  );
}
class Hello extends Component {
  render() {
    return React.createElement("h1", null, "Hello, ", this.props.name, "!");
  }
}

function Jello({ name }) {
  return React.createElement("h1", null, "Jello, ", name, "!");
}

function App() {
  return React.createElement(
    React.Fragment,
    null,
    React.createElement(Hello, {
      name: "Sarah",
    }),
    React.createElement(Jello, {
      name: "Connor",
    }),
  );
}

Here name="Connor" simply becomes an option { name: "Connor" }.

Components that contain wrap components can be defined using the children attribute that we've already seen in the transpiled output:

JSX Code Transpiled output (Babel)
const Wrapper = (props) => {
  return (
    <div style={{ backgroundColor: 'lightgray', padding: '20px', border: '1px solid black' }}>
      <h2>Inside the Wrapper:</h2>
      {props.children}
    </div>
  );
};
<Wrapper>
  <p>Paragraph</p>
  <p>Another one!</p>
</Wrapper>
const Wrapper = (props) => {
  return React.createElement(
    "div",
    {
      style: {
        backgroundColor: "lightgray",
        padding: "20px",
        border: "1px solid black",
      },
    },
    React.createElement("h2", null, "Inside the Wrapper:"),
    props.children,
  );
};

React.createElement(
  Wrapper,
  null,
  React.createElement("p", null, "Paragraph"),
  React.createElement("p", null, "Another one!"),
);

Valid JSX tag names are valid JSX identifiers (variable names), which mean that hypenated XML and HTML tags can be used without issue. However, custom components cannot have hyphenated names due to limitation from ECMAScript grammar.[2]

JavaScript expressions

[edit]

JavaScript expressions (but not statements) can be used inside JSX with curly brackets {}:[4]: 14–16 

JSX Code Transpiled output (Babel)
<h1>{10 + 1}</h1>
React.createElement("h1", null, 10 + 1);

The example above will render:

<h1>11</h1>

Conditional expressions

[edit]

If–else statements cannot be used inside JSX but conditional expressions can be used instead. The example below will render { i === 1 ? 'true' : 'false' } as the string 'true' because i is equal to 1.

TSX Code Transpiled output (Babel)
import React from 'react';

const App: React.FC = () => {
    const i: number = 1;

    return (
        <div>
            <h1>{ i === 1 ? 'true' : 'false' }</h1>
        </div>
    );
}

export default App;
import React from 'react';
const App = () => {
  const i = 1;
  return (
    React.createElement(
      "div",
      null,
      React.createElement("h1", null, i === 1 ? "true" : "false"),
    )
  );
};
export default App;

The above will render:

<div>
    <h1>true</h1>
</div>

Functions and JSX can be used in conditionals:[4]: 88–90 

TSX Code Transpiled output (Babel)
import React from 'react';

const App: React.FC = () => {
    const sections: number[] = [1, 2, 3];

    return (
        <div>
            {sections.map((n, i) => (
                /* 'key' is a React-specific attribute for tracking of list items and their changes */
                /* Each 'key' must be unique */
                <div key={"section-" + n}>
                    Section {n} {i === 0 && <span>(first)</span>}
                </div>
            ))}
        </div>
    );
}

export default App;
import React from 'react';

const App = () => {
  const sections = [1, 2, 3];
  return React.createElement(
    "div",
    null,
    sections.map((n, i) =>
      React.createElement(
        "div",
        { key: "section-" + n },
        "Section ",
        n,
        " ",
        i === 0 && React.createElement("span", null, "(first)"),
      ),
    ),
  );
};

export default App;

The above will render:

<div>
    <div>Section 1 <span>(first)</span></div>
    <div>Section 2</div>
    <div>Section 3</div>
</div>

Babel: pragmas

[edit]

Transpilers such as Babel ofter the ability to target function calls other than those from React. Here Babel is directed to call tag and frag instead:

TSX Code Transpiled output (Babel)
// @jsx tag
// @jsxfrag frag
const App = () => {
  const sections = [1, 2, 3];
  return (
    <div>
      {sections.map((n, i) => (
        <div key={"section-" + n}>
          Section {n} {i === 0 && <span>(first)</span>}
        </div>
      ))}
    </div>
  );
};
// @jsx tag
// @jsxfrag frag
const App = () => {
  const sections = [1, 2, 3];
  return tag(
    "div",
    null,
    sections.map((n, i) =>
      tag("div", { key: "section-" + n }, "Section ", n, " ",
          i === 0 && tag("span", null, "(first)")),
    ),
  );
};

With different versions of tag and frag, different representations of the input can be produced:

JS Code Program output
// Object tree.
// Fragments are represented as arrays
function frag(...children) {
  return children;
}

// Tags are represented as objects
function tag(name, attrs, ...children) {
  // Flattening the array unpacks the fragments
  // Still need to deal with "false"
  const kids = []
  for (x of children.flat(Infinity)) {
    if (x === false || x === null || x === undefined) {
      if (kids.at(-1) === " ") kids.pop()
    }
    else kids.push(x)
  }
  return {name, children: kids, ...(attrs && { attrs })}
}

console.log(JSON.stringify(App()));
{
  "name": "div",
  "children": [
    {
      "name": "div",
      "attrs": { "key": "section-1" },
      "children": [
        "Section ", 1, " ",
        { "name": "span", "children": ["(first)"] }
      ]
    },
    {
      "name": "div",
      "attrs": { "key": "section-2" },
      "children": ["Section ", 2]
    },
    {
      "name": "div",
      "attrs": { "key": "section-3" },
      "children": ["Section ", 3]
    }
  ]
}
// Pug-like output
function frag(...children) {
  let kids = [];
  for (const x of children) {
    // Text start with |
    if (!Array.isArray(x)) kids.push("| " + x);  
    else kids.push(...x.flat(Infinity));
  }
}

function tag(name, attrs, ...children) {
  const indent = "  ";
  const attrStr = Object.entries(attrs || {})
    .map(([k, v]) => `${k}="${v}"`)
    .join(", ");
  const head = `${name}${attrStr ? `(${attrStr})` : ""}`;
  let kids = [];
  for (const x of frag(children)) {
    if (x === false) {
      if (kids.at(-1) === "|  ") kids.pop();
    } else kids.push(x);
  }
  if (kids.length == 1)
    // Shorthand syntax for elem>text and for element nesting
    return kids[0].startsWith("| ")
      ? [`${head} ${kids[0].substr(2)}`]
      : [`${head}: ${kids[0]}`];
  return [head, ...kids.map((line) => indent + line)];
}

console.log(App().join('\n'));
div
  div(key="section-1")
    | Section 
    | 1
    |  
    span (first)
  div(key="section-2")
    | Section 
    | 2
  div(key="section-3")
    | Section 
    | 3

Dialects

[edit]

The above covers the conversion from JSX to ECMAScript code. As indicated in the "pragma" section, the ECMAScript code that receives JSX-derived objects are free to do whatever it wants to these data:

  • Frontend applications such as React generally translate these data structures into function calls that manipulate the Document Object Model of a page. These libraries also have server-side rendering variants that, via manipulating a "browserless" DOM, produces an ordinary HTML or XML webpage by serializing the DOM.[13]
  • Backend applications may opt to generate HTML or XML directly by piecing together strings, effectively acting as a template engine.[14]
  • More niche applications (demonstrated above) may not have anything to do with HTML or XML entirely.

As far as HTML and XML are invovled, the most common areas of divergence are the handling of attributes and the setup of available components.

Attributes

[edit]

Names

[edit]

Although JSX defines how attributes should be parsed – and Babel maintains a convention about how it should be conveyed to other ECMAScript code – neither define how these attributes map to actual HTML and XML attributes. For example, React uses the following convention to minimize the need for the {... spread} syntax:[15]

  • Plain alphanumerical attribute names are unchanged.
  • Hyphenated attribute names from HTML are converted to camelCase, with the exception of data-* and aria-*. This is in line with the JavaScript DOM API.
  • The xlink:* attribute names are converted to camelCase, e.g. xink:href becomes xlinkHref.

React's DOM renderer has a preset list of attributes that it knows about. Some of these are for conversion to HTML/XML, others are for its internal use. In the production (non-development) mode, only these attributes are allowed on output tags unless the tag is "svg" or "math".[16]

Values

[edit]

The HTML style attribute applies a collection of CSS styles to the element with it. As an ergonomic measure, React-DOM not only accepts passing this value in string form, but also as a JavaScript object. Instead of <div style={"color:" + clr}>, one could write <div style={{ color: clr }}>. The implementation of the "style" function automatically translates the object to the correct string format. A similar camelcase-renaming operation is done to simplify the use of styles with hyphenated names.[17]

React-DOM also allows passing JavaScript functions instead of strings as values for the event handler attributes.[17]

Components

[edit]

Depending on the output langauge, different components (which appear like HTML/XML "tags") may be available. For example, the HTML tags in React come from react-dom, which is not used when targeting a different langauge. The runtime may also predefine some components: <Fragment> is one such example, though it has become universal via Babel's convention.[18]

See also

[edit]

References

[edit]
  1. ^ "Initial commit of the website · facebook/jsx@c40e9b6". GitHub.
  2. ^ a b c "Draft: JSX Specification". JSX. Facebook. Retrieved 7 April 2018.
  3. ^ Larsen, John (2021). React Hooks in Action With Suspense and Concurrent Mode. Manning. ISBN 978-1720043997.
  4. ^ a b c d Wieruch, Robin (14 September 2018). The Road to React. Leanpub. ISBN 978-1720043997.
  5. ^ "Documentation - JSX". www.typescriptlang.org. Retrieved 2025-07-13.
  6. ^ Fischer, Ludovico (2017-09-06). React for Real: Front-End Code, Untangled. Pragmatic Bookshelf. ISBN 9781680504484.
  7. ^ Larsen, John (2021). React Hooks in Action With Suspense and Concurrent Mode. Manning. ISBN 978-1720043997.
  8. ^ a b "@babel/plugin-transform-react-jsx · Babel". babeljs.io.
  9. ^ "compiler-notations-spec/pure-notation-spec.md at main · javascript-compiler-hints/compiler-notations-spec". GitHub.
  10. ^ Clark, Andrew (September 26, 2017). "React v16.0§New render return types: fragments and strings". React Blog.
  11. ^ "React.Component: render". React.
  12. ^ Clark, Andrew (September 26, 2017). "React v16.0§Support for custom DOM attributes". React Blog.
  13. ^ "createRoot – React". react.dev.
  14. ^ "html/benchmarks at master · kitajs/html". GitHub.
  15. ^ "React DOM Components – React". react.dev.
  16. ^ "react/packages/react-dom-bindings/src/client/ReactDOMComponent.js at 64b4605cb82367fa78a08b99bbee1c800e6af21a · facebook/react". GitHub.
  17. ^ a b "Common components (e.g. ) – React". react.dev.
  18. ^ "Built-in React Components – React". react.dev.
[edit]