The basics
What is a TSX file?
A .tsx file is a TypeScript file that may contain JSX. JSX is a syntax extension, created for React, that lets you write user-interface markup in the middle of code: <button onClick={save}>Save</button> instead of a chain of function calls. The .tsx extension is how the TypeScript compiler knows to expect it: in a plain .ts file, JSX is a syntax error.
Most .tsx files hold one or more components for a React app, a Next.js site or a similar project. Like every TypeScript file, it can't run as it is. A compiler or bundler removes the types and turns each tag into a JavaScript function call, and the result runs in the browser.
- JSX isn't HTML. It's closer to JavaScript: attributes are props,
{…}holds expressions, every tag must be closed (<br />), and a component returns a single element or a fragment<>…</>. - JSX isn't part of JavaScript. Its specification says it isn't meant to be implemented by browsers or engines, nor proposed for ECMAScript; it's meant for compilers to transform.
- TSX isn't only React. The tags compile to whatever functions you configure, so Preact, Solid and other libraries use
.tsxfiles too.
Anatomy
What's inside a TSX file
A TSX file is TypeScript in which an expression can also be a JSX element: a tag like <section>, with attributes and children, that a compiler turns into a function call. Here is a typical React component, part by part.
import { useState, type ReactNode } from "react";
type Props = {
title: string;
initial?: number;
children?: ReactNode;
};
export function Counter({ title, initial = 0, children }: Props) {
const [count, setCount] = useState<number>(initial);
return (
<section className="counter">
<h2>{title}</h2>
{children}
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
</section>
);
}
export const First = <T,>({ items }: { items: T[] }) => (
<p>First: {String(items[0])}</p>
);- 1
import { useState, type ReactNode } from "react";Imports
A hook and a type from React. With the automatic JSX runtime, the file doesn't need to import
Reactitself just to use tags. - 2
type Props = { … };The props type
Describes the attributes the component accepts.
<Counter />without atitle, or withinitial="5"as a string, is a compile error. - 3
children?: ReactNode;Children
Whatever goes between
<Counter>and</Counter>.ReactNodecovers text, numbers, elements, lists and nothing. - 4
export function Counter(…)A component
A function that takes props and returns JSX. Its name must start with a capital letter: lowercase tags are treated as HTML elements.
- 5
useState<number>(initial)A typed hook
TypeScript generics work as usual in a
.tsxfile when they follow a function name. - 6
<section className="counter">A JSX element
Looks like HTML but is an expression. In React, attributes use their DOM property names:
classNamerather thanclass,htmlForrather thanfor. - 7
{title}An embedded expression
Curly braces hold any TypeScript expression. Text inserted this way is escaped, so it can't inject HTML.
- 8
onClick={() => setCount(count + 1)}An event handler
A function, not a string of code as in HTML. TypeScript knows the event's type, so
event.currentTargetis anHTMLButtonElement. - 9
<T,>({ items }: { items: T[] }) =>A generic arrow function
The comma after
Tis needed in.tsxonly: without it,<T>is read as the start of a JSX tag.
Under the hood
What the tags in a TSX file compile to
Each JSX element becomes a call to a function, with the tag name, an object of props and the children. Which function depends on the jsx compiler option. Here is the Counter component above, compiled by tsc with the two React settings:
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from "react";
export function Counter({ title, initial = 0, children }) {
const [count, setCount] = useState(initial);
return (_jsxs("section", { className: "counter", children: [_jsx("h2", { children: title }), children, _jsxs("button", { onClick: () => setCount(count + 1), children: ["Clicked ", count, " times"] })] }));
}return (React.createElement("section", { className: "counter" },
React.createElement("h2", null, title),
children,
React.createElement("button", { onClick: () => setCount(count + 1) },
"Clicked ",
count,
" times")));| "jsx" setting | Output | File written |
|---|---|---|
react-jsx | _jsx() calls, with the import from react/jsx-runtime added automatically | .js |
react-jsxdev | _jsxDEV() calls with extra debugging information | .js |
react | React.createElement() calls; React must be in scope | .js |
preserve | The JSX left as it is, for another tool to compile | .jsx |
react-native | The JSX left as it is | .js |
The automatic runtime arrived with React 17 in 2020 and is supported from TypeScript 4.1; it's what new React projects use. The classic React.createElement form still works, and it's what "jsx": "react" produces, which is why older files start with import React from "react".
Configuration
Setting up tsconfig.json for React, Preact or Solid
Two options in tsconfig.json decide how .tsx files are checked and compiled: jsx picks the output form, and jsxImportSource names the library whose runtime and types to use.
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
"strict": true,
"module": "esnext",
"moduleResolution": "bundler",
"noEmit": true
}
}| Library | "jsx" | "jsxImportSource" | Note |
|---|---|---|---|
| React | react-jsx | react (the default) | Install @types/react for the types |
| Preact | react-jsx | preact | Preact accepts class as well as className |
| Solid | preserve | solid-js | Solid compiles JSX with its own plugin, so TypeScript leaves it alone |
A single file can override the project with a comment at the top: /** @jsxImportSource preact */ changes the library, and /** @jsxRuntime classic */ with /** @jsx h */ makes every tag call your own h() function. The sample-no-react.tsx file below uses this to run without any library.
Bundlers such as esbuild and Vite compile .tsx without checking types. Run tsc --noEmit as well, so type errors in components are caught.
Syntax
What works differently in .tsx than in .ts
Because < can now start a tag, a few TypeScript forms that use angle brackets clash with JSX:
| In a .ts file | In a .tsx file | Why |
|---|---|---|
const id = <T>(x: T) => x; | const id = <T,>(x: T) => x; | <T> at the start of an expression is read as an opening JSX tag. The trailing comma (or <T extends unknown>) makes it a type parameter. Generic function declarations are unaffected |
const n = <number>value; | const n = value as number; | Angle-bracket type assertions aren't allowed in .tsx; as works in both |
| No JSX | <div>…</div>, <Card /> | Lowercase names are HTML elements, checked against JSX.IntrinsicElements; capitalised names must be components in scope |
Only use .tsx for files that contain JSX. Keeping utility code in .ts files avoids these clashes and makes it clear which files depend on a UI library.
Related formats
.tsx vs .ts vs .jsx vs .js
| Extension | Types | JSX | Typical content |
|---|---|---|---|
.tsx | Yes | Yes | Components written in TypeScript |
| `.ts` | Yes | No (a syntax error) | Logic, utilities, server code |
| `.jsx` | No | Yes | Components written in JavaScript |
| `.js` | No | Only if the build tool is configured for it | Everything else; the output of compiling all of the above |
Moving a component from .jsx to .tsx is a rename followed by adding types for its props. The tags stay exactly the same.
How to
How to run, build and open a TSX file
- Open or edit it: in VS Code, which understands TSX without plugins, or WebStorm, Zed or any other code editor. Any text editor shows it as plain text.
- Build it for the browser: with the tool your project uses, such as Vite (
npm run dev,npm run build), Next.js ornpx esbuild app.tsx --bundle. They all compile.tsxout of the box. - Check types:
npx tsc --noEmitwith atsconfig.jsonthat setsjsx, ornpx tsc --noEmit --jsx react-jsx file.tsxfor one file. - Run it in a terminal: Bun (
bun file.tsx) and Deno run TSX directly, and so does the tsx package on Node.js (npx tsx file.tsx). Node.js itself can strip types from.tsfiles but doesn't support.tsx. - Try it online: the TypeScript Playground on typescriptlang.org accepts TSX and shows the errors and the compiled JavaScript.
tsx the tool vs .tsx the file
The tsx npm package ("TypeScript Execute") is a command that runs TypeScript files on Node.js. It shares the name, and can run .tsx files, but it's not the file format.
Troubleshooting
The TSX errors people hit most, and what they mean
| Error message | Usual cause | Fix |
|---|---|---|
| TS17004: Cannot use JSX unless the '--jsx' flag is provided | No jsx setting in tsconfig.json, or tsc file.tsx run without one | Add "jsx": "react-jsx" (or your library's setting) |
| React is not defined | The classic runtime turned tags into React.createElement(), but the file never imports React | Switch to the automatic runtime ("jsx": "react-jsx"), or add import React from "react" |
| TS2686: 'React' refers to a UMD global, but the current file is a module | "jsx": "react" in a file that doesn't import React | Use "jsx": "react-jsx" |
| TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists | The library's types aren't installed or found | npm i -D @types/react (or set jsxImportSource for another library) |
| TS17008: JSX element 'T' has no corresponding closing tag | A generic arrow function written as <T>(…) => | Write <T,>(…) => |
| Unknown file extension ".tsx" | Running a .tsx file with plain Node.js (outside an ES module project it shows as SyntaxError: Unexpected token '<') | Use npx tsx file.tsx or Bun, or build it first |
Security
Is it safe to open a TSX file?
Opening a .tsx file in an editor shows text and runs nothing. Running or building one runs code, and building a project also runs its build tools and npm packages, with your permissions. Treat a downloaded project like any program.
In the code itself, React escapes any text you insert with {…}, so a user's name can't turn into HTML or a script. The escape hatch, dangerouslySetInnerHTML, skips that protection: only pass it HTML you have sanitised or fully control, or it becomes a cross-site scripting hole.
Free downloads
Sample TSX files to download
Free sample TSX files for testing compilers, bundlers, editors, syntax highlighters, upload filters and size limits. Each .tsx file passes tsc --noEmit in strict mode and runs with tsx and Bun with the same output.
Loading…What's inside sample-component.tsx
The file is commented: open it in a text editor and each numbered comment marks one of these parts. With React installed, npx tsx sample-component.tsx prints the HTML it renders.
The first line: the JSX runtime
/** @jsxRuntime automatic */makes this one file use React's automatic runtime, so it runs even without atsconfig.json.Comments 1 to 3: imports, props and a component
Type-only imports with the
typekeyword, aBadgePropsinterface with a union-typedtone, and aBadgecomponent with a default prop value.Comment 4: JSX syntax
className, a template literal inside an attribute, and{…}for values and children.Comments 5 and 6: a generic component
Listis written<T,>(…) =>so the type parameter isn't mistaken for a tag, and gives each item akey.Comment 7: state and events
useState<number>and a click handler typed asMouseEvent<HTMLButtonElement>.Comment 8: type assertions
JSON.parse(…) as { … }; the older<Type>valueform would be a syntax error in a.tsxfile.Comments 9 and 10: composition and rendering
A fragment, a conditional, and
renderToStaticMarkupto print HTML in a terminal instead of drawing in a browser.
All sample TSX files
Commented React components
sample-component.tsx · 3.3 KB (3,391 bytes)
Typed props, a generic list component, state, an event handler, a fragment and conditional rendering, with numbered comments explaining each part. Needs
react,react-domand their@typespackages to run and check.- Checking a bundler or editor handles TSX and generics
- Teaching typed React components
TSX with no library
sample-no-react.tsx · 2.7 KB (2,807 bytes)
Uses
@jsxRuntime classicand@jsx hcomments to compile its tags to calls to a smallh()function that builds escaped HTML. Runs withnpx tsxorbunwithout installing anything, and declares its ownJSXtypes.- Testing JSX pragmas and custom factories
- Seeing what JSX compiles to without React
sample-component.tsx after compiling
sample-compiled.js · 3.3 KB (3,355 bytes)
The featured file compiled by TypeScript 7.0.2 with
--jsx react-jsx: no types, and every tag a_jsx()or_jsxs()call. Runs withnodewhere React is installed.- Comparing TSX with its JavaScript output
- Checking a minifier or linter on compiled React code
100 KB TSX file
sample-100kb.tsx · 100 KB (102,400 bytes)
1,800 typed colour swatches rendered into an HTML table by a tiny JSX factory, padded with comments to exactly 102,400 bytes. Needs no npm packages.
- Upload and size-limit tests
- Compiler and highlighter performance
Ways to test with them
- JSX settings: run
sample-no-react.tsxthrough your bundler. If it fails whilenpx tsxworks, the tool is ignoring the@jsxpragma comments. - Generics:
sample-component.tsxcontains a<T,>arrow function; tools that parse.tsxas plain TypeScript, or.tsas TSX, will choke on one or the other. - Compiled output: compile
sample-component.tsxwith your settings and compare it withsample-compiled.js. - Upload filters and limits:
.tsxis unknown to many upload filters.sample-100kb.tsxis exactly 102,400 bytes, for testing a limit at the boundary.
Free to use for testing, teaching and demos, commercially or not, with no attribution needed. A link back to this page is always appreciated.
Put it online
Have a TSX file? Give it a link.
Drop a .tsx file on 99helpers and it gets its own address in seconds. Opening the link shows the code as plain text in the browser, and it never runs, which is what you want for sharing a component's source.
Questions
TSX files, frequently asked
What is a .tsx file?
A TypeScript file that contains JSX, the HTML-like tags used by React and similar libraries. It usually holds user-interface components.
What's the difference between .ts and .tsx?
Only .tsx files may contain JSX tags. In exchange, .tsx files can't use the <Type>value assertion form, and generic arrow functions need a comma: <T,>.
What's the difference between .tsx and .jsx?
.tsx is TypeScript with JSX, so it has type annotations that a compiler checks. .jsx is JavaScript with JSX and has no types.
How do I open a .tsx file?
In any code editor, such as VS Code, or a plain-text editor. It's text. To see what it renders, it has to be built as part of a React (or similar) project.
Can I run a .tsx file with Node.js?
Not directly: Node.js strips types from .ts files but doesn't support .tsx. Use npx tsx file.tsx, Bun or Deno, or compile it first.
Do I need to import React in a .tsx file?
Not with the automatic runtime ("jsx": "react-jsx"), which new projects use. With the classic "jsx": "react" setting, every file with JSX must import React.
Why does my generic arrow function break in a .tsx file?
<T>(x: T) => x looks like the start of a JSX tag. Write <T,>(x: T) => x or <T extends unknown>(x: T) => x.
Is TSX only for React?
No. The tags compile to whatever functions you configure, so Preact, Solid and other libraries use .tsx files too, each with its own jsxImportSource.
Related formats
Other file types explained
Keep going
Related
Sources
- TypeScript Handbook: JSX
- TSConfig reference: jsx
- React blog: Introducing the New JSX Transform (2020)
- JSX specification (draft)
- Solid documentation: TypeScript
- Node.js documentation: Modules, TypeScript
Last reviewed 27 October 2026.