Per-File JSX Factories in TypeScript
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 module.
#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 article and 44 others are part of the TypeScript Evolution series. Have a look!