The basics
What is a JSX file?
A .jsx file holds JavaScript that uses JSX, a syntax extension created at Facebook (now Meta) for React. JSX lets you write user-interface markup where JavaScript expects a value: const title = <h1>Hello</h1>;. The .jsx extension tells editors and build tools to expect those tags.
Most .jsx files contain React components: App.jsx, Button.jsx, main.jsx. Preact, Solid and other libraries use JSX too. The TypeScript version, with types, is the `.tsx` file.
- JSX isn't HTML. It turns into JavaScript function calls, so attributes are values (
count={3}is a number), names follow the DOM (className), and every tag must be closed. - JSX isn't a template language. There are no special loops or
ifblocks: you use JavaScript's ownmap,? :and&&inside{…}. - JSX isn't standard JavaScript. Its specification says it isn't meant for browsers or engines to implement, nor proposed for ECMAScript. Something has to compile it first.
Anatomy
What's inside a JSX file
A JSX file is ordinary JavaScript in which an expression can also be an element written as a tag. Here is a small React component, a to-do list, part by part.
import { useState } from "react";
import "./TodoList.css";
export default function TodoList({ initial = [] }) {
const [items, setItems] = useState(initial);
const [text, setText] = useState("");
function add(event) {
event.preventDefault();
if (text.trim()) setItems([...items, text.trim()]);
setText("");
}
return (
<form onSubmit={add} className="todo">
<label htmlFor="todo-text">New item</label>
<input id="todo-text" value={text}
onChange={(e) => setText(e.target.value)} />
{items.length === 0 ? (
<p>Nothing to do yet.</p>
) : (
<ul>
{items.map((item) => <li key={item}>{item}</li>)}
</ul>
)}
</form>
);
}- 1
import { useState } from "react";An ES module import
JSX files are almost always ES modules.
useStateis a React hook; with the automatic JSX runtime the file doesn't importReactitself to use tags. - 2
import "./TodoList.css";A stylesheet import
Not JavaScript at all: a bundler such as Vite or webpack understands it and adds the CSS to the page. Without a bundler this line fails.
- 3
export default function TodoList({ initial = [] })A component
A function whose name starts with a capital letter. Its one argument holds the props, unpacked here with a default value.
- 4
const [items, setItems] = useState(initial);State
A value React keeps between renders. Calling
setItemsstores a new value and redraws the component. - 5
event.preventDefault();An event handler
Plain JavaScript: stops the browser submitting the form and reloading the page.
- 6
return ( <form …> … </form> );The returned JSX
A component returns one root element (or a fragment
<>…</>). The brackets only let the JSX span several lines. - 7
className="todo" / htmlFor="todo-text"DOM property names
In React,
classandforare writtenclassNameandhtmlFor, the names the browser's DOM uses, becauseclassandforare JavaScript keywords. - 8
<input … />A self-closing tag
Every element must be closed in JSX, including ones HTML leaves open, like
<input>,<img>and<br>. - 9
{items.length === 0 ? ( … ) : ( … )}A condition
Curly braces hold any JavaScript expression.
ifstatements can't go inside JSX, so conditions use? :or&&. - 10
<li key={item}>A list with keys
mapturns an array into elements. Thekeylets React tell items apart when the list changes.
Under the hood
Why browsers can't run a .jsx file, and what it compiles to
To a browser or Node.js, <form onSubmit={add}> is a syntax error: < is the less-than operator. Before the code can run, a compiler replaces each element with a function call that takes the tag name, an object of props and the children. Here is the return statement of the anatomy example, compiled by Babel with React's automatic runtime:
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// …
return /*#__PURE__*/_jsxs("form", {
onSubmit: add,
className: "todo",
children: [/*#__PURE__*/_jsx("label", {
htmlFor: "todo-text",
children: "New item"
}), /*#__PURE__*/_jsx("input", {
id: "todo-text",
value: text,
onChange: e => setText(e.target.value)
}), items.length === 0 ? /*#__PURE__*/_jsx("p", {
children: "Nothing to do yet."
}) : /*#__PURE__*/_jsx("ul", {
children: items.map(item => /*#__PURE__*/_jsx("li", {
children: item
}, item))
})]
});The /*#__PURE__*/ comments tell minifiers each call can be dropped if its result is unused. The older classic runtime compiles to React.createElement instead, which is why older JSX files start with import React from "react":
return React.createElement("form", { onSubmit: add, className: "todo" },
React.createElement("label", { htmlFor: "todo-text" }, "New item"),
…React introduced the automatic runtime with React 17 in 2020 and Babel 7.9. Current Babel (version 8) uses it by default; esbuild still defaults to the classic form unless you pass --jsx=automatic or set it in tsconfig.json. The output is plain JavaScript that runs anywhere React is loaded.
For quick demos, @babel/standalone can compile JSX inside the browser from a <script type="text/babel"> element. It downloads the whole compiler and compiles on every page load, so real sites compile ahead of time.
Tools
Compiling JSX with Babel, esbuild, Vite and others
| Tool | Command or setup | JSX in .js files? |
|---|---|---|
| Babel | npx babel app.jsx --presets @babel/preset-react | Yes; Babel goes by its configuration, not the extension |
| esbuild | npx esbuild app.jsx --bundle --jsx=automatic | Only with --loader:.js=jsx |
| Vite | Handles .jsx and .tsx out of the box (npm create vite@latest, React template) | Not by default |
TypeScript (tsc) | "allowJs": true and a "jsx" setting in tsconfig.json | Yes, with allowJs |
| Bun | bun app.jsx runs it directly | Yes |
| Node.js | Can't run .jsx; compile first, or use npx tsx app.jsx | No |
Vite's own React template names its files main.jsx and App.jsx. Whatever the tool, the output is the same kind of file: plain JavaScript in which every tag has become a function call.
Naming
.jsx vs .js: which extension to use
Both conventions exist. Create React App and many older projects put JSX in .js files and configured their tools to accept it. Vite, esbuild and most current setups expect JSX in .jsx (or .tsx) files and plain JavaScript in .js, and the popular ESLint React plugin has a rule, react/jsx-filename-extension, that enforces the same.
| Convention | Typical files | Why |
|---|---|---|
JSX only in .jsx | App.jsx, Button.jsx, api.js, utils.js | Tools and readers know which files contain markup; fastest builds |
JSX in .js | App.js, Button.js | Older React projects; needs the build tool configured |
| TypeScript | App.tsx, api.ts | Same split, with types; the default in many new projects |
Component files are usually named after the component, in PascalCase (TodoList.jsx exports TodoList), with one main component per file. Renaming .js to .jsx changes nothing in the code; check that imports which spell out the extension are updated.
Same extension
The other .jsx: Adobe ExtendScript
Adobe apps such as Photoshop, Illustrator and InDesign run automation scripts written in ExtendScript, Adobe's older dialect of JavaScript, and those scripts also use the .jsx extension. They have nothing to do with React. Telling them apart:
| React JSX | Adobe ExtendScript | |
|---|---|---|
| Contains | <tags>, import, components | No tags; app.activeDocument, File, alert() |
| Often starts with | import lines | A #target photoshop (or other app) line |
| Runs in | A browser, after compiling | The Adobe app: File > Scripts |
| Compiled form | .js | .jsxbin (binary) |
An ExtendScript .jsx file is a full program inside the Adobe app: it can read, write and delete files on your computer. Only run scripts from sources you trust.
Troubleshooting
The JSX errors people hit most, and what they mean
| Error message | Usual cause | Fix |
|---|---|---|
| SyntaxError: Unexpected token '<' | A .jsx file (or JSX in a .js file) loaded straight into a browser or Node.js | Compile it with a build tool first |
| Unknown file extension ".jsx" | node file.jsx in an ES module project | Build it first, or run it with npx tsx file.jsx or Bun |
| The JSX syntax extension is not currently enabled | esbuild (or Vite) reading JSX in a .js file | Rename it to .jsx, or pass --loader:.js=jsx |
| Support for the experimental syntax 'jsx' isn't currently enabled | Babel without @babel/preset-react | Add the preset to the Babel configuration |
| React is not defined | The classic runtime turned tags into React.createElement(), but the file doesn't import React | Use the automatic runtime, or add import React from "react" |
| Adjacent JSX elements must be wrapped in an enclosing tag | A component returning two sibling elements | Wrap them in a parent element or a fragment <>…</> |
How to
How to open and edit a JSX file, and is it safe?
- Edit it: VS Code highlights and checks JSX without plugins; WebStorm, Zed, Sublime Text and other code editors support it too. Any text editor can open it.
- See what it renders: it has to run inside a project:
npm create vite@latest, pick React, put the component insrc, and runnpm run dev. - Run a single file in a terminal:
bun file.jsx, ornpx tsx file.jsxon Node.js. The samples below print their HTML this way. - Convert it to plain JavaScript:
npx babel file.jsx --presets @babel/preset-reactornpx esbuild file.jsx --jsx=automatic.
Opening a .jsx file in an editor runs nothing. A React project's build runs its tools and npm packages with your permissions, so treat a downloaded project like any program. In the code, React escapes text inserted with {…}; dangerouslySetInnerHTML skips that, so only pass it HTML you have sanitised or fully control. And remember that an Adobe .jsx script is a program for Photoshop or Illustrator, not a web component.
Free downloads
Sample JSX files to download
Free sample JSX files for testing compilers, bundlers, syntax highlighters, encodings, upload filters and size limits. Each was compiled with Babel or esbuild and run with Bun and, where Node.js allows, tsx, with matching output.
Loading…What's inside sample-app.jsx
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-app.jsx prints the HTML it renders.
The header and pragma
How to set up, run and build the file, and a
/** @jsxRuntime automatic */comment so it compiles the same way in every tool.Comments 1 and 2: imports and a component
Only
useStateis imported from React.Greetingtakes props with a default value forlang.Comments 3 and 4: JSX syntax
className, a self-closing<br />, an expression choosing the greeting, and a comment written inside JSX with{/* … */}.Comment 5: lists
mapover an array of menu items, each<li>with akey.Comment 6: state and events
A like button with
useState, anonClickarrow function and anaria-pressedattribute.Comments 7 and 8: composition and rendering
A fragment holding several components,
&&to show the menu only when it has items, andrenderToStaticMarkupto print HTML in a terminal.
All sample JSX files
Commented React components
sample-app.jsx · 2.6 KB (2,704 bytes)
Props, lists with keys, state, an event handler, a fragment and conditional rendering, with numbered comments explaining each part. Needs
reactandreact-dominstalled to run.- Checking a bundler, linter or highlighter handles JSX
- Teaching React components
sample-app.jsx after compiling
sample-compiled.js · 3.3 KB (3,427 bytes)
The featured file compiled by Babel 8 with
@babel/preset-reactfor production: the same comments, and every tag now a_jsx()or_jsxs()call.- Seeing what JSX turns into
- Comparing your build's output
JSX in a .js file
sample-jsx-in-js.js · 1.2 KB (1,262 bytes)
JSX in a file with a
.jsextension, as some projects do.nodeand a default esbuild reject it;esbuild --loader:.js=jsxbuilds it. Uses a tinyh()factory, so it needs no React.- Testing how tools treat JSX in `.js` files
- Checking lint rules about file extensions
UTF-8 with BOM and CRLF
sample-utf8.jsx · 1.5 KB (1,499 bytes)
Text in ten languages, including right-to-left Arabic and Hebrew, emoji, and HTML entities inside JSX (
€, ), saved with a UTF-8 byte order mark and Windows line endings. Needs no React.- Spotting encoding bugs in compilers and pipelines
- Testing BOM and CRLF handling
100 KB JSX file
sample-100kb.jsx · 100 KB (102,400 bytes)
A table of 823 Latin, Greek and Cyrillic letters with their Unicode names, rendered by a tiny JSX factory and 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
- Encodings: run
sample-utf8.jsxthrough your build and compare the output. AnyÃorâ€means a step didn't read it as UTF-8; a strayat the start means the byte order mark wasn't stripped. - Loader settings:
sample-jsx-in-js.jsshould fail in a default esbuild or Vite setup and build once JSX is enabled for.js. - Compiler output: compile
sample-app.jsxwith your settings and compare it withsample-compiled.js. - Upload filters and limits: some filters block
.jsx(it's also an Adobe script extension).sample-100kb.jsxis 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 JSX file? Give it a link.
Drop a .jsx 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
JSX files, frequently asked
What is a .jsx file?
Usually a JavaScript file that contains JSX, the HTML-like tags used by React. It holds user-interface components and is compiled to plain JavaScript before it runs. Some .jsx files are Adobe ExtendScript scripts instead.
What does JSX stand for?
It's commonly expanded as JavaScript XML, after the XML-like syntax of its tags. React's documentation just calls it JSX, a syntax extension for JavaScript.
How do I open a .jsx file?
In any code editor, such as VS Code, or a plain-text editor: it's text. To see what it renders, run it as part of a React project, for example one created with Vite.
Can a browser run a .jsx file?
No. Browsers only understand JavaScript, and the tags are a syntax error to them. A build tool such as Vite, esbuild or Babel compiles JSX to JavaScript first.
Should I use .jsx or .js for React components?
.jsx is the safer choice today: Vite and esbuild expect JSX in .jsx files by default. JSX in .js files works only where the build tool is configured for it, as in Create React App.
What's the difference between .jsx and .tsx?
.tsx is the TypeScript version: the same tags, plus type annotations checked by the TypeScript compiler.
How do I convert a .jsx file to .js?
Compile it: npx babel file.jsx --presets @babel/preset-react or npx esbuild file.jsx --jsx=automatic. Renaming it to .js doesn't remove the tags.
Why does my Photoshop script have a .jsx extension?
Adobe apps use .jsx for ExtendScript, their own scripting dialect of JavaScript. Those files have nothing to do with React, and they run inside the Adobe app.
Related formats
Other file types explained
Keep going
Related
Sources
- JSX specification (draft)
- React blog: Introducing the New JSX Transform (2020)
- TypeScript Handbook: JSX
- Vite documentation: Features (JSX and TypeScript)
- Adobe Illustrator Scripting Guide: Scripting language support
Last reviewed 27 October 2026.