I’m attempting to add support for JSX or TSX files to an Ember 3.28 app. Based on ember-react-components, I tried configuring my ember-cli-build.js as follows:
babel: {
spec: true,
plugins: [
require.resolve("@babel/plugin-syntax-jsx"),
require.resolve("@babel/plugin-transform-react-jsx"),
],
},
"ember-cli-babel": {
enableTypeScriptTransform: true,
extensions: ["js", "ts", "jsx"],
},
However, once I add a .jsx file, I get a build error from TypeScript:
test.jsx: Type parameter list cannot be empty. (2:9)
1 | export function TestJsx() {
> 2 | return <>Hello world!</>
| ^
3 | }
4 |SyntaxError: /Users/matthew/repos/cc/v4/frontend/cc-frontend/utils/test.jsx: Type parameter list cannot be empty. (2:9)
1 | export function TestJsx() {
> 2 | return <>Hello world!</>
| ^
3 | }
4 |
at constructor (/Users/matthew/repos/cc/v4/frontend/node_modules/@babel/parser/lib/index.js:359:19)
at TypeScriptParserMixin.raise ...
I believe that ember-cli-typescript is parsing the JSX before @babel/plugin-transform-react-jsx can, causing it to complain at the (valid) JSX that is invalid TypeScript.
I get the same error if I use a .tsx file and configure TypeScript to process it ("jsx": "react-jsx" in tsconfig.json and “tsx” in ember-cli-babel.extensions). I tried adding .jsx files to my tsconfig.json’s exclude list but it does not help.
Any ideas for how to get this working? I guess I could opt out of ember-cli-babel’s auto-generated config, but I’ve had trouble getting that to work with TypeScript.
If I do opt out of ember-cli-babel’s auto-generated config, I run into this build error (before adding anything jsx related):
Build Error (StripBadReexports)
[BABEL] unknown file: Preset /* your preset */ requires a filename to be set when babel is called directly,
babel.transformSync(code, { filename: 'file.ts', presets: [/* your preset */] });
See https://babeljs.io/docs/en/options#filename for more information.
The stack trace points to this line in ember-composable-helpers (v4.4.1). I can understand what the error is saying, but I’m confused why it only fails after I opt out.
Here is my babel.config.js (based on GitHub - emberjs/ember-cli-babel: Ember CLI plugin for Babel):
//babel.config.js
const { buildEmberPlugins } = require("ember-cli-babel")
module.exports = function (api) {
api.cache(true)
return {
presets: [
[
require.resolve("@babel/preset-env"),
{
targets: require("./config/targets"),
spec: true,
},
],
[require.resolve("@babel/preset-typescript"), { allowDeclareFields: true }],
],
plugins: [
// this is where all the ember required plugins would reside
...buildEmberPlugins(__dirname, {
/*customOptions if you want to pass in */
}),
],
}
}
Okay, I got this working with a fairly standard ember-cli-babel/TypeScript setup. The key change is to explicitly list @babel/plugin-transform-typescript in babel.plugins so that you can add the isTsx: true option, overriding the options set by enableTypeScriptTransform: true.
Otherwise, you just need to install and specify plugins equivalent to babel-preset-react.
Here is the relevant part of my ember-cli-build.js:
babel: {
plugins: [
[require.resolve("@babel/plugin-syntax-jsx")],
[require.resolve("@babel/plugin-transform-react-jsx"), { runtime: "automatic" }],
[require.resolve("@babel/plugin-transform-react-display-name")],
[
require.resolve("@babel/plugin-transform-typescript"),
{ allowDeclareFields: true, isTSX: true },
],
],
},
"ember-cli-babel": {
enableTypeScriptTransform: true,
extensions: ["js", "ts", "jsx", "tsx"],
},
You may be interested in this react in ember app I worked on! GitHub - JackHowa/ai-react-ember: A React in Ember app that also shows how to do an incremental ai migration with Vertex AI
There’s other ai stuff going on but the resolver works with react
Maybe I should demo AI converting React to Ember Polaris
<3
1 Like
did it: @nullvoxpopuli.com on Bluesky
it also fixes a11y issues
4 Likes
Has anyone gotten this working with Embroider + Vite? I’ve tried various ways to configure the React + Babel vite plugins without luck - Vite claims that it can’t find any .tsx files that I import.
Perhaps I need to give up on @vitejs/plugin-react and just pass everything through Babel like before? Though I fear I’ll lose HMR for React components.
After some experimentation, here is a vite.config.mjs that appears to work with Embroider + Vite, in an Ember 3.28 app. I have not tested HMR yet.
Note that this is for Vite v7; v8 will be different because it no longer uses esbuild.
import { classicEmberSupport, ember, extensions } from "@embroider/vite"
import { babel } from "@rollup/plugin-babel"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
resolve: {
// Include .tsx/.jsx so bare imports like "./MyReactComponent" resolve to .tsx files.
extensions: [...extensions, ".tsx", ".jsx"],
},
plugins: [
classicEmberSupport(),
ember(),
babel({
babelHelpers: "runtime",
extensions,
}),
// Include Vite's standard React plugin.
react(),
// Ember + React compat trick:
// Ember wants to build TypeScript with Babel, while React wants to build it with esbuild.
// Tell the latter to apply to .jsx/.tsx only.
{
name: "limit-esbuild-to-jsx",
enforce: "post",
config: () => ({ esbuild: { include: /\.[jt]sx$/ } }),
},
],
})
React HMR works so long as I place the React components outside of the /app directory (which Embroider appears to handle specially).
you’ll need to go back to babel for the react compiler, I think 
don’t know if they’ve done a faster version yet