Was this page helpful?

JSX

JSX is an embeddable XML-like syntax.It is meant to be transformed into valid JavaScript, though the semantics of that transformation are implementation-specific.JSX rose to popularity with theReact framework, but has since seen other implementations as well.TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript.

Basic usage

In order to use JSX you must do two things.

  1. Name your files with a.tsx extension
  2. Enable thejsx option

TypeScript ships with several JSX modes:preserve,react (classic runtime),react-jsx (automatic runtime),react-jsxdev (automatic development runtime), andreact-native.Thepreserve mode will keep the JSX as part of the output to be further consumed by another transform step (e.g.Babel).Additionally the output will have a.jsx file extension.Thereact mode will emitReact.createElement, does not need to go through a JSX transformation before use, and the output will have a.js file extension.Thereact-native mode is the equivalent ofpreserve in that it keeps all JSX, but the output will instead have a.js file extension.

ModeInputOutputOutput File Extension
preserve<div /><div />.jsx
react<div />React.createElement("div").js
react-native<div /><div />.js
react-jsx<div />_jsx("div", {}, void 0);.js
react-jsxdev<div />_jsxDEV("div", {}, void 0, false, {...}, this);.js

You can specify this mode using either thejsx command line flag or the corresponding optionjsx in your tsconfig.json file.

*Note: You can specify the JSX factory function to use when targeting react JSX emit withjsxFactory option (defaults toReact.createElement)

Theas operator

Recall how to write a type assertion:

ts
constfoo = <Foo>bar;

This asserts the variablebar to have the typeFoo.Since TypeScript also uses angle brackets for type assertions, combining it with JSX’s syntax would introduce certain parsing difficulties. As a result, TypeScript disallows angle bracket type assertions in.tsx files.

Since the above syntax cannot be used in.tsx files, an alternate type assertion operator should be used:as.The example can easily be rewritten with theas operator.

ts
constfoo =barasFoo;

Theas operator is available in both.ts and.tsx files, and is identical in behavior to the angle-bracket type assertion style.

Type Checking

In order to understand type checking with JSX, you must first understand the difference between intrinsic elements and value-based elements.Given a JSX expression<expr />,expr may either refer to something intrinsic to the environment (e.g. adiv orspan in a DOM environment) or to a custom component that you’ve created.This is important for two reasons:

  1. For React, intrinsic elements are emitted as strings (React.createElement("div")), whereas a component you’ve created is not (React.createElement(MyComponent)).
  2. The types of the attributes being passed in the JSX element should be looked up differently.Intrinsic element attributes should be knownintrinsically whereas components will likely want to specify their own set of attributes.

TypeScript uses thesame convention that React does for distinguishing between these.An intrinsic element always begins with a lowercase letter, and a value-based element always begins with an uppercase letter.

TheJSX namespace

JSX in TypeScript is typed by theJSX namespace. TheJSX namespace may be defined in various places, depending on thejsx compiler option.

Thejsx optionspreserve,react, andreact-native use the type definitions for classic runtime. This means a variable needs to be in scope that’s determined by thejsxFactory compiler option. TheJSX namespace should be specified on the top-most identifier of the JSX factory. For example, React uses the default factoryReact.createElement. This means itsJSX namespace should be defined asReact.JSX.

ts
exportfunctioncreateElement():any;
exportnamespaceJSX {
// …
}

And the user should always import React asReact.

ts
import*asReactfrom'react';

Preact uses the JSX factoryh. That means its types should be defined as theh.JSX.

ts
exportfunctionh(props:any):any;
exportnamespaceh.JSX {
// …
}

The user should use a named import to importh.

ts
import {h }from'preact';

For thejsx optionsreact-jsx andreact-jsxdev, theJSX namespace should be exported from the matching entry points. Forreact-jsx this is${jsxImportSource}/jsx-runtime. Forreact-jsxdev, this is${jsxImportSource}/jsx-dev-runtime. Since these don’t use a file extension, you must use theexports field inpackage.json map in order to support ESM users.

json
{
"exports": {
"./jsx-runtime":"./jsx-runtime.js",
"./jsx-dev-runtime":"./jsx-dev-runtime.js",
}
}

Then injsx-runtime.d.ts andjsx-dev-runtime.d.ts:

ts
exportnamespaceJSX {
// …
}

Note that while exporting theJSX namespace is sufficient for type checking, the production runtime needs thejsx,jsxs, andFragment exports at runtime, and the development runtime needsjsxDEV andFragment. Ideally you add types for those too.

If theJSX namespace isn’t available in the appropriate location, both the classic and the automatic runtime fall back to the globalJSX namespace.

Intrinsic elements

Intrinsic elements are looked up on the special interfaceJSX.IntrinsicElements.By default, if this interface is not specified, then anything goes and intrinsic elements will not be type checked.However, if this interfaceis present, then the name of the intrinsic element is looked up as a property on theJSX.IntrinsicElements interface.For example:

tsx
declarenamespaceJSX {
interfaceIntrinsicElements {
foo:any;
}
}
<foo/>;// ok
<bar/>;// error

In the above example,<foo /> will work fine but<bar /> will result in an error since it has not been specified onJSX.IntrinsicElements.

Note: You can also specify a catch-all string indexer onJSX.IntrinsicElements as follows:

ts
declarenamespaceJSX {
interfaceIntrinsicElements {
[elemName:string]:any;
}
}

Value-based elements

Value-based elements are simply looked up by identifiers that are in scope.

tsx
importMyComponentfrom"./myComponent";
<MyComponent/>;// ok
<SomeOtherComponent/>;// error

There are two ways to define a value-based element:

  1. Function Component (FC)
  2. Class Component

Because these two types of value-based elements are indistinguishable from each other in a JSX expression, first TS tries to resolve the expression as a Function Component using overload resolution. If the process succeeds, then TS finishes resolving the expression to its declaration. If the value fails to resolve as a Function Component, TS will then try to resolve it as a class component. If that fails, TS will report an error.

Function Component

As the name suggests, the component is defined as a JavaScript function where its first argument is aprops object.TS enforces that its return type must be assignable toJSX.Element.

tsx
interfaceFooProp {
name:string;
X:number;
Y:number;
}
declarefunctionAnotherComponent(prop: {name:string });
functionComponentFoo(prop:FooProp) {
return<AnotherComponentname={prop.name}/>;
}
constButton = (prop: {value:string },context: {color:string })=> (
<button/>
);

Because a Function Component is simply a JavaScript function, function overloads may be used here as well:

ts
interfaceClickableProps {
children:JSX.Element[] |JSX.Element;
}
 
interfaceHomePropsextendsClickableProps {
home:JSX.Element;
}
 
interfaceSidePropsextendsClickableProps {
side:JSX.Element |string;
}
 
functionMainButton(prop:HomeProps):JSX.Element;
functionMainButton(prop:SideProps):JSX.Element;
functionMainButton(prop:ClickableProps):JSX.Element {
// ...
}
Try

Note: Function Components were formerly known as Stateless Function Components (SFC). As Function Components can no longer be considered stateless in recent versions of react, the typeSFC and its aliasStatelessComponent were deprecated.

Class Component

It is possible to define the type of a class component.However, to do so it is best to understand two new terms: theelement class type and theelement instance type.

Given<Expr />, theelement class type is the type ofExpr.So in the example above, ifMyComponent was an ES6 class the class type would be that class’s constructor and statics.IfMyComponent was a factory function, the class type would be that function.

Once the class type is established, the instance type is determined by the union of the return types of the class type’s construct or call signatures (whichever is present).So again, in the case of an ES6 class, the instance type would be the type of an instance of that class, and in the case of a factory function, it would be the type of the value returned from the function.

ts
classMyComponent {
render() {}
}
// use a construct signature
constmyComponent =newMyComponent();
// element class type => MyComponent
// element instance type => { render: () => void }
functionMyFactoryFunction() {
return {
render: ()=> {},
};
}
// use a call signature
constmyComponent =MyFactoryFunction();
// element class type => MyFactoryFunction
// element instance type => { render: () => void }

The element instance type is interesting because it must be assignable toJSX.ElementClass or it will result in an error.By defaultJSX.ElementClass is{}, but it can be augmented to limit the use of JSX to only those types that conform to the proper interface.

tsx
declarenamespaceJSX {
interfaceElementClass {
render:any;
}
}
classMyComponent {
render() {}
}
functionMyFactoryFunction() {
return {render: ()=> {} };
}
<MyComponent/>;// ok
<MyFactoryFunction/>;// ok
classNotAValidComponent {}
functionNotAValidFactoryFunction() {
return {};
}
<NotAValidComponent/>;// error
<NotAValidFactoryFunction/>;// error

Attribute type checking

The first step to type checking attributes is to determine theelement attributes type.This is slightly different between intrinsic and value-based elements.

For intrinsic elements, it is the type of the property onJSX.IntrinsicElements

tsx
declarenamespaceJSX {
interfaceIntrinsicElements {
foo: {bar?:boolean };
}
}
// element attributes type for 'foo' is '{bar?: boolean}'
<foobar/>;

For value-based elements, it is a bit more complex.It is determined by the type of a property on theelement instance type that was previously determined.Which property to use is determined byJSX.ElementAttributesProperty.It should be declared with a single property.The name of that property is then used.As of TypeScript 2.8, ifJSX.ElementAttributesProperty is not provided, the type of first parameter of the class element’s constructor or Function Component’s call will be used instead.

tsx
declarenamespaceJSX {
interfaceElementAttributesProperty {
props;// specify the property name to use
}
}
classMyComponent {
// specify the property on the element instance type
props: {
foo?:string;
};
}
// element attributes type for 'MyComponent' is '{foo?: string}'
<MyComponentfoo="bar"/>;

The element attribute type is used to type check the attributes in the JSX.Optional and required properties are supported.

tsx
declarenamespaceJSX {
interfaceIntrinsicElements {
foo: {requiredProp:string;optionalProp?:number };
}
}
<foorequiredProp="bar"/>;// ok
<foorequiredProp="bar"optionalProp={0}/>;// ok
<foo/>;// error, requiredProp is missing
<foorequiredProp={0}/>;// error, requiredProp should be a string
<foorequiredProp="bar"unknownProp/>;// error, unknownProp does not exist
<foorequiredProp="bar"some-unknown-prop/>;// ok, because 'some-unknown-prop' is not a valid identifier

Note: If an attribute name is not a valid JS identifier (like adata-* attribute), it is not considered to be an error if it is not found in the element attributes type.

Additionally, theJSX.IntrinsicAttributes interface can be used to specify extra properties used by the JSX framework which are not generally used by the components’ props or arguments - for instancekey in React. Specializing further, the genericJSX.IntrinsicClassAttributes<T> type may also be used to specify the same kind of extra attributes just for class components (and not Function Components). In this type, the generic parameter corresponds to the class instance type. In React, this is used to allow theref attribute of typeRef<T>. Generally speaking, all of the properties on these interfaces should be optional, unless you intend that users of your JSX framework need to provide some attribute on every tag.

The spread operator also works:

tsx
constprops = {requiredProp:"bar" };
<foo{...props}/>;// ok
constbadProps = {};
<foo{...badProps}/>;// error

Children Type Checking

In TypeScript 2.3, TS introduced type checking ofchildren.children is a special property in anelement attributes type where childJSXExpressions are taken to be inserted into the attributes.Similar to how TS usesJSX.ElementAttributesProperty to determine the name ofprops, TS usesJSX.ElementChildrenAttribute to determine the name ofchildren within those props.JSX.ElementChildrenAttribute should be declared with a single property.

ts
declarenamespaceJSX {
interfaceElementChildrenAttribute {
children: {};// specify children name to use
}
}
tsx
<div>
<h1>Hello</h1>
</div>;
<div>
<h1>Hello</h1>
World
</div>;
constCustomComp = (props)=><div>{props.children}</div>
<CustomComp>
<div>Hello World</div>
{"This is just a JS expression..."+1000}
</CustomComp>

You can specify the type ofchildren like any other attribute. This will override the default type from, e.g. theReact typings if you use them.

tsx
interfacePropsType {
children:JSX.Element
name:string
}
classComponentextendsReact.Component<PropsType, {}> {
render() {
return (
<h2>
{this.props.children}
</h2>
)
}
}
// OK
<Componentname="foo">
<h1>Hello World</h1>
</Component>
// Error: children is of type JSX.Element not array of JSX.Element
<Componentname="bar">
<h1>Hello World</h1>
<h2>Hello World</h2>
</Component>
// Error: children is of type JSX.Element not array of JSX.Element or string.
<Componentname="baz">
<h1>Hello</h1>
World
</Component>

The JSX result type

By default the result of a JSX expression is typed asany.You can customize the type by specifying theJSX.Element interface.However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface.It is a black box.

The JSX function return type

By default, function components must returnJSX.Element | null. However, this doesn’t always represent runtime behaviour. As of TypeScript 5.1, you can specifyJSX.ElementType to override what is a valid JSX component type. Note that this doesn’t define what props are valid. The type of props is always defined by the first argument of the component that’s passed. The default looks something like this:

ts
namespaceJSX {
exporttypeElementType =
// All the valid lowercase tags
|keyofIntrinsicElements
// Function components
| (props:any)=>Element
// Class components
|new (props:any)=>ElementClass;
exportinterfaceIntrinsicAttributesextends/*...*/ {}
exporttypeElement =/*...*/;
exporttypeElementClass =/*...*/;
}

Embedding Expressions

JSX allows you to embed expressions between tags by surrounding the expressions with curly braces ({ }).

tsx
consta = (
<div>
{["foo","bar"].map((i)=> (
<span>{i/2}</span>
))}
</div>
);

The above code will result in an error since you cannot divide a string by a number.The output, when using thepreserve option, looks like:

tsx
consta = (
<div>
{["foo","bar"].map(function (i) {
return<span>{i/2}</span>;
})}
</div>
);

React integration

To use JSX with React you should use theReact typings.These typings define theJSX namespace appropriately for use with React.

tsx
///<referencepath="react.d.ts"/>
interfaceProps {
foo:string;
}
classMyComponentextendsReact.Component<Props, {}> {
render() {
return<span>{this.props.foo}</span>;
}
}
<MyComponentfoo="bar"/>;// ok
<MyComponentfoo={0}/>;// error

Configuring JSX

There are multiple compiler flags which can be used to customize your JSX, which work as both a compiler flag and via inline per-file pragmas. To learn more see their tsconfig reference pages:

The TypeScript docs are an open source project. Help us improve these pagesby sending a Pull Request

Contributors to this page:
MHMohamed Hegazy  (55)
OTOrta Therox  (20)
RCRyan Cavanaugh  (6)
DZDavid Zulaica  (3)
KTKanchalai Tanglertsampan  (3)
35+

Last updated: Jul 14, 2025