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:
- TypeScript 2.0: Non-Nullable Types
- TypeScript 2.0: Control Flow Based Type Analysis
- TypeScript 2.0: Acquiring Type Declaration Files
- TypeScript 2.0: Read-Only Properties
- TypeScript 2.0: Tagged Union Types
- TypeScript 2.0: More Literal Types
- TypeScript 2.0: The never Type
- TypeScript 2.0: Built-In Type Declarations
- TypeScript 2.1: async/await for ES3/ES5
- TypeScript 2.1: External Helpers Library
- TypeScript 2.1: Object Rest and Spread
- TypeScript 2.1: keyof and Lookup Types
- TypeScript 2.1: Mapped Types
- TypeScript 2.1: Improved Inference for Literal Types
- TypeScript 2.1: Literal Type Widening
- TypeScript 2.1: Untyped Imports
- TypeScript 2.2: The object Type
- TypeScript 2.2: Dotted Properties and String Index Signatures
- TypeScript 2.2: Null-Checking for Expression Operands
- TypeScript 2.2: Mixin Classes
- TypeScript 2.3: Generic Parameter Defaults
- TypeScript 2.3: The --strict Compiler Option
- TypeScript 2.3: Type-Checking JavaScript Files with --checkJs
- TypeScript 2.3: Downlevel Iteration for ES3/ES5
- TypeScript 2.4: String Enums
- TypeScript 2.4: Weak Type Detection
- TypeScript 2.4: Spelling Correction
- TypeScript 2.4: Dynamic import() Expressions
- TypeScript 2.5: Optional catch Binding
- TypeScript 2.6: JSX Fragment Syntax
- TypeScript 2.7: Numeric Separators
- TypeScript 2.7: Strict Property Initialization
- TypeScript 2.8: Per-File JSX Factories