Headshot of Marius Schulz
Marius Schulz Front End Engineer

TypeScript 2.6: JSX Fragment Syntax

TypeScript 2.6 added support for JSX fragments. Within .tsx files, you can now use the new <>...</> syntax to create a fragment.

Motivation Behind JSX Fragments

In React, it's a common pattern to return multiple elements from a component. For instance, let's say we want to render multiple list items within the following component:

class List extends React.Component {
  render() {
    return (
      <ul>
        <ListItems />
        <li>Item 3</li>
      </ul>
    );
  }
}

However, in our ListItems component, we cannot simply return multiple adjacent JSX elements like this:

class ListItems extends React.Component {
  render() {
    return (
      <li>Item 1</li>
      <li>Item 2</li>
    );
  }
}

Adjacent JSX elements must be wrapped in an enclosing element, so we could add a wrapping div element:

class ListItems extends React.Component {
  render() {
    return (
      <div>
        <li>Item 1</li>
        <li>Item 2</li>
      </div>
    );
  }
}

Unfortunately, adding such a wrapper breaks the structure of our list. Our ListItems component currently renders the following DOM elements:

<ul>
  <div>
    <li>Item 1</li>
    <li>Item 2</li>
  </div>
  <li>Item 3</li>
</ul>

Note that the <div> doesn't belong in there. We can get rid of it by using the JSX fragment syntax instead:

class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

A fragment lets us group multiple JSX elements without adding an extra wrapper node. Now, our List component renders the expected markup:

<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

Alternatively, we could've explicitly written React.Fragment instead of using the new JSX syntax:

class ListItems extends React.Component {
  render() {
    return (
      <React.Fragment>
        <li>Item 1</li>
        <li>Item 2</li>
      </React.Fragment>
    );
  }
}

The two versions of our ListItems component are equivalent and render exactly the same output (given that we compile our JSX for use with React).

Compiling JSX Fragments with TypeScript

Here's our ListItems component with the new JSX syntax again:

class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

If we compile the .tsx file with --jsx react (and --target es2015), the following JavaScript is emitted:

class ListItems extends React.Component {
  render() {
    return (
      React.createElement(
        React.Fragment,
        null,
        React.createElement("li", null, "Item 1"),
        React.createElement("li", null, "Item 2")
      )
    );
  }
}

The compiler replaces the short fragment syntax by a call to the React.createElement() method and passes it React.Fragment as the first argument.

If we compiled our ListItems component with --jsx preserve (and --target es2015) instead, our JSX would be emitted unchanged, set aside whitespace:

class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

This post is part of the TypeScript Evolution series:

  1. TypeScript 2.0: Non-Nullable Types
  2. TypeScript 2.0: Control Flow Based Type Analysis
  3. TypeScript 2.0: Acquiring Type Declaration Files
  4. TypeScript 2.0: Read-Only Properties
  5. TypeScript 2.0: Tagged Union Types
  6. TypeScript 2.0: More Literal Types
  7. TypeScript 2.0: The never Type
  8. TypeScript 2.0: Built-In Type Declarations
  9. TypeScript 2.1: async/await for ES3/ES5
  10. TypeScript 2.1: External Helpers Library
  11. TypeScript 2.1: Object Rest and Spread
  12. TypeScript 2.1: keyof and Lookup Types
  13. TypeScript 2.1: Mapped Types
  14. TypeScript 2.1: Improved Inference for Literal Types
  15. TypeScript 2.1: Literal Type Widening
  16. TypeScript 2.1: Untyped Imports
  17. TypeScript 2.2: The object Type
  18. TypeScript 2.2: Dotted Properties and String Index Signatures
  19. TypeScript 2.2: Null-Checking for Expression Operands
  20. TypeScript 2.2: Mixin Classes
  21. TypeScript 2.3: Generic Parameter Defaults
  22. TypeScript 2.3: The --strict Compiler Option
  23. TypeScript 2.3: Type-Checking JavaScript Files with --checkJs
  24. TypeScript 2.3: Downlevel Iteration for ES3/ES5
  25. TypeScript 2.4: String Enums
  26. TypeScript 2.4: Weak Type Detection
  27. TypeScript 2.4: Spelling Correction
  28. TypeScript 2.4: Dynamic import() Expressions
  29. TypeScript 2.5: Optional catch Binding
  30. TypeScript 2.6: JSX Fragment Syntax
  31. TypeScript 2.7: Numeric Separators
  32. TypeScript 2.7: Strict Property Initialization
  33. TypeScript 2.8: Per-File JSX Factories
  34. TypeScript 2.8: Conditional Types
  35. TypeScript 2.8: Mapped Type Modifiers