TypeScript 2.8: Per-File JSX Factories
TypeScript 2.8 allows you to specify JSX factory names on a per-file basis. Previously, you could only specify the JSX factory name via the --jsxFactory compiler option. This setting applies to each JSX file in the entire project. Now, you can override the project-wide --jsxFactory setting by adding a special @jsx comment to the beginning of the file.
Let's say we want to use Preact to render the string "Hello World!" into the <div id="app"> container. Preact uses the h function to create JSX elements. We can add the special /** @jsx h */ comment (also known as a "pragma") at the beginning of our .tsx file:
/** @jsx h */
import { h, render } from "preact";
render(
<h1>Hello World!</h1>,
document.getElementById("app")!
);
With the /** @jsx h */ pragma in place, the compiler will emit the following JavaScript code for the above file:
/** @jsx h */
import { h, render } from "preact";
render(
h("h1", null, "Hello World!"),
document.getElementById("app")
);
Here's the tsconfig.json file that I used to compile the code:
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"jsx": "react",
"strict": true
}
}
Note that the compiler only recognizes the pragma if you use the /** ... */ block comment syntax. It won’t change the JSX factory setting if you use the // ... single-line comment syntax.
What is a JSX Factory?
JSX is not part of the ECMAScript standard; that is, it is not valid JavaScript on its own. A script or module that contains JSX therefore can't run directly in the browser. Just like files with type annotations, JSX files need to be compiled to plain JavaScript files first. The --jsxFactory option tells the TypeScript compiler how exactly it should compile JSX elements.
Notice how <h1>Hello World!</h1> was transformed into h("h1", null, "Hello World!"). Preact uses the function h to create virtual DOM elements, which is why we specified h as the JSX factory name. We also need to import h from the preact package so that it is available within the scope of the module:
import { h, /* other imports */ } from "preact";
Specifying the JSX Factory Per File vs. Per Project
So when do we need to specify the JSX factory on a per-file basis? If you only use JSX with a single JavaScript library in your project, you don't need a per-file configuration. In this case, it is easier to change the --jsxFactory option within tsconfig.json so that it applies to all JSX files in your project:
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"jsx": "react",
"jsxFactory": "h",
"strict": true
}
}
By default, the --jsxFactory option is set to React.createElement when using the --jsx react option. Therefore, if you’re using React, you don't need to specify the --jsxFactory option at all, nor do you have to add the /** @jsx ... */ pragma.
The per-file configuration of the JSX factory is useful if you're using multiple JavaScript libraries with JSX in the same project. For instance, you might want to add a Vue component to a web application that is written primarily in React. The /** @jsx ... */ pragma allows you to specify a different JSX factory for these files without having to have multiple tsconfig.json files.
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