Skip to content

JSX Support #56822

Closed as not planned
Closed as not planned

Description

@arthurfiorette

What is the problem this feature will solve?

Following up on nodejs/node#56322 and nodejs/node#56392.

JSX and its various flavors have undergone frequent configuration changes since their inception. While supporting JSX without any configuration might seem ideal, it is both impractical and problematic. Just as "type": "module" was introduced to address CommonJS/ESM interoperability, we must continue relying on existing configuration files—tsconfig.json or jsconfig.json—to determine the correct transformation to apply.

The presence or absence of JSX should not rely on node's recent work for supporting typescript.


JSX has multiple targets, primarily react and react-jsx, which produce different outputs from the same input. For example:

<h1>Hello world</h1>
  • react-jsx

    import { jsx as _jsx } from "react/jsx-runtime";
    _jsx("h1", { children: "Hello world" });
  • react Output

    import React from "react";
    React.createElement("h1", null, "Hello world");
  • jsxFactory – Specifies the function to replace createElement when using the react target. For example, setting jsxFactory: 'h' replaces React.createElement with h.

  • jsxFragmentFactory – Determines the function used for fragments (<></>). If set, fragments will use the specified name instead of React.Fragment.

  • jsxImportSource – Used with the react-jsx target to change the import source. For example, setting jsxImportSource: 'preact' results in:

    import { jsx as _jsx } from "preact/jsx-runtime";

Their combination is what is needed for any library that supports JSX, such as Vue.js, Kita, React, Preact, Astro, and others...


Given the diversity of JavaScript frameworks, adopting a default configuration favoring any single framework would inevitably create issues for others.

One alternative is to add fields to package.json to define some JSX settings, eliminating the need for a jsconfig.json or tsconfig.json file. However, rewriting this (ideal or not) standard is not something the community will like.


In the long run, adding support for a native JSX transform function, similar to Deno’s implementation, could significantly enhance Node.js performance in SSR scenarios.

Deno's native transform has demonstrated 7-20× faster rendering times and 50% reduction in garbage collection overhead. Such improvements would benefit SSR frameworks like Next.js, positioning Node.js competitively once again.

What is the feature you are proposing to solve the problem?

I propose that nodejs transform JSX syntax and support .jsx and .tsx files.

What alternatives have you considered?

No response

Activity

  1. marco-ippolito commented on Jan 30, 2025

    @marco-ippolito
    Member

    I was exploring this idea.
    We could create a hook that framework can implement.
    Example
    Every frameworks can do something like:

    module.registerJsxHook((obj) =>{
    React.createElement(// logic to convert object into react)
    })
    <h1>Hello world</h1>

    Is transpiled into:

    process.getBuiltInModule('module').jsxHandler({
    // The object transformed
    })
  2. ljharb commented on Jan 30, 2025

    @ljharb
    SponsorMember

    What happens when there's more than one hook registered in an application, as would be virtually guaranteed to happen?

    There's a reason jsx isn't in the language yet, or in any platform.

  3. marco-ippolito commented on Jan 30, 2025

    @marco-ippolito
    Member

    What happens when there's more than one hook registered in an application, as would be virtually guaranteed to happen?

    Care to explain why?
    Also

    There's a reason jsx isn't in the language yet, or in any platform.

    Expand pls

  4. ljharb commented on Jan 30, 2025

    @ljharb
    SponsorMember

    What I mean is, the rules for the syntax are pretty universal and clear - the problem is that there's a plethora of transformations of it. If you had to pick only one, you'd pick React's - but which one? The pre-17 one, that's way more widely used, or the 17+ one, that's the future of React?

    If you only pick one, then you ace out anyone using an alternative transformation.


    If you allow it to be customizable, and it's not customizable per module (which would require syntax, for ESM, and thus TC39 support - although with CJS you could do it by just injecting a new function into scope), then you've created a global state and capability that means that any code that can set it, can override the meaning of what appears to be syntax - which is a massive security hole and attack vector.

    In other words, I think that the only way to support jsx that's even remotely reasonable here is with a loader, and users can already use custom loaders to achieve it - so "node supports jsx" would just mean that node ships a loader by default, which would mean node is picking a winner - and, if node's picking a winner, per the usage data, it's going to be React, and that's not exactly fair to React competitors.


    There may come a time in the future when the JS language can answer some of these questions - or when userland has largely settled on a single jsx transformation - but until that time, it feels at best incredibly risky and worst the precursor to an abject disaster for node to attempt to address this itself.

  5. marco-ippolito commented on Jan 30, 2025

    @marco-ippolito
    Member

    @ljharb I understand the technical issues, but I don't agree with marking a problem as "won't fix" without exploring it. This has been one major historical problem of the loaders space, which has burned out several people. While I understand its not an easy fix, it would be more useful to talk about blockers and requirements for this to happen. The jsx support is something that can be fixed, just like typescript support, and we shoud look into fixing it

  6. arthurfiorette commented on Jan 30, 2025

    @arthurfiorette
    Author

    or when userland has largely settled on a single jsx transformation

    A single transformation target is not what JSX needs.

    Any attempt to standardize it into a single format seems prone to failure. JSX community has already proven different syntaxes are better for different use cases:

    • deno precompile
    • react-jsx
    • jsx native
    • and so on...

    then you've created a global state and capability that means that any code that can set it

    jsx: react-jsx prefers a importable module following some rules.

    jsx: react requires React to be available in local scope, the following code is valid:

    function template(React) {
      return <div></div>
    }

    And that's part of how JSX is supposed to be.

  7. ljharb commented on Jan 30, 2025

    @ljharb
    SponsorMember

    exactly. And when there’s multiple valid ways of handling a syntax, the existing solution for that is “custom loaders”.

  8. koshic commented on Jan 30, 2025

    @koshic

    exactly. And when there’s multiple valid ways of handling a syntax, the existing solution for that is “custom loaders”.

    Those arguments are 100% applicable to TypeScript support too. But, Node.js team finally decided to choose one option (SWC in amaro) and implement basic support level keeping hooks only for advanced scenarios. What is the difference between JSX and TS? Both can be compiled by various ways, both can have overcomplicated external configuration, both are tightly coupled with 3rd-party ecosystem, etc.

    JSX / TS / support in Node.js just a step towards the community, because base framework functionality more than enough to implement any possible solution by using third-party tools / libraries / even native addons.

    What I want to say - JSX support is mostly not a technical question. May be it's need to have TSC decision or something like that.

  9. ljharb commented on Jan 30, 2025

    @ljharb
    SponsorMember

    I'm not aware of an alternative semantic meaning for compiled TS output - it's JavaScript. Babel, tsc, swc, all compile TS with different approaches that result in the same runtime JS. JSX is vastly different, and the various approaches result in widely varying runtime behavior.

  10. marco-ippolito commented on Jan 30, 2025

    @marco-ippolito
    Member

    I'm not aware of an alternative semantic meaning for compiled TS output - it's JavaScript. Babel, tsc, swc, all compile TS with different approaches that result in the same runtime JS. JSX is vastly different, and the various approaches result in widely varying runtime behavior.

    it is possible to standardize the transpilation to a certain point and have the framework hook in with some Node API. We probably should ask framework maintainers.

    For example:

    <div></div>
    

    is transpiled to:

    // Dont mind the content of the object
    process.getBuiltInModule('module').jsxHandler({ tag: 'div' });
    

    The framework can register a hook to do something with the object that is passed: { tag: 'div' }

  11. koshic commented on Jan 30, 2025

    @koshic

    Babel, tsc, swc, all compile TS with different approaches that result in the same runtime JS

    It's not quite true - generated code is different even for the one particular tool.

    For example, tsc has a lot of flags that directly affect JS output: preserveConstEnums, downlevelIteration, importHelpers, noUncheckedSideEffectImports, esModuleInterop, verbatimModuleSyntax, importsNotUsedAsValues, preserveValueImports, etc.

    SWC has even more. This is why current state of TS support in Node is a compromise with some defaults and this is why we have 'Full TypeScript support' chapter in the documentation.

  12. ljharb commented on Jan 30, 2025

    @ljharb
    SponsorMember

    That is indeed how it could technically be achieved - but then we've got global state (the current JSX handler) and apps could have multiple JSX transpilations in the same application, even ignoring third party dependencies.

  13. 10 remaining items

  14. github-actions commented on Dec 6, 2025

    @github-actions
    Contributor

    There has been no activity on this feature request for 5 months. To help maintain relevant open issues, please add the never-stale Issues and PRs exempt from automated stale handling. label or close this issue if it should be closed. If not, the issue will be automatically closed 6 months after the last non-automated comment.
    For more information on how the project manages feature requests, please consult the feature request management document.

  15. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Dec 6, 2025
  16. arthurfiorette commented on Dec 8, 2025

    @arthurfiorette
    Author

    JSX is part of today's ecosystem. JSX being framework agnostic implies that multiple stuff might be using jsx at the same time. People can use it to render xml, html strings, email templates and anything else they ever want. Even if big frameworks will need to keep transpiling JSX in the future, not a problem! Still, Implementing native support brings simple use-cases much more compelling to the scene.

    The same way TS support was added for simplicity, since it was possible to run TS code before and a lot of people will keep using transpilers in their process of running TS code in the future, JSX should be handled the same way.

    Specially now that we have amaro set up and, besides settling down on settings/DX, this should not be as hard as it was to set up TS support in the first place.

  17. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Dec 8, 2025
  18. mertcanaltin commented on Dec 28, 2025

    @mertcanaltin
    Member

    Hello, I tried to make a plan regarding this place What do you think?

    My plan:

    Add --experimental-jsx flag,
    Use amaro (already supports JSX via SWC),
    Read config from tsconfig.json/jsconfig.json,
    Default to react-jsx transform,
    Support per-file /** @jsxImportSource */ pragma

    Update: I discussed this with @marco-ippolito on Slack and understand the concerns:

    1. Node.js can't parse tsconfig (architectural decision)
    2. JSX is not standardized yet
    3. Requires sourcemap support (adds complexity)

    Thanks for the feedback everyone!

  19. AugustinMauroy commented on Dec 28, 2025

    @AugustinMauroy
    Member

    I don't see how to include JSX support how of the box to node.js
    imo the official answer should be use customization api.

  20. ljharb commented on Dec 28, 2025

    @ljharb
    SponsorMember

    While I'd love to see node support jsx, I think that until it is a standard, it wouldn't make much sense.

  21. AugustinMauroy commented on Dec 28, 2025

    @AugustinMauroy
    Member

    While I'd love to see node support jsx, I think that until it is a standard, it wouldn't make much sense.

    meta already write something https://facebook.github.io/jsx/ we use that as basis of a real thing. Maybe open an issue at CPC to create an openjs wg

  22. bjohansebas commented on Dec 28, 2025

    @bjohansebas
    Member

    meta already write something https://facebook.github.io/jsx/ we use that as basis of a real thing. Maybe open an issue at CPC to create an openjs wg

    The standards group is already, which could presumably handle making it a standard on behalf of OpenJS.

  23. ljharb commented on Dec 28, 2025

    @ljharb
    SponsorMember

    That's only the syntax - it doesn't talk about the transformation itself, which is why every tool that works with JS makes up their own (and even React changed it in v17). A transformation would have to be designed, agreed upon, and adopted across existing tools, before it would make sense for anyone to try to standardize JSX.

  24. Renegade334 commented on Mar 3, 2026

    @Renegade334
    Member

    --experimental-transform-types is being removed, for reasons outlined in nodejs/typescript#51. I cannot see that the same arguments would not apply to an amaro-based JSX transformer.

    For my $0.02, this and #56822 (comment) are enough to make this wontfix.

  25. AugustinMauroy commented on Mar 3, 2026

    @AugustinMauroy
    Member

    --experimental-transform-types is being removed, for reasons outlined in nodejs/typescript#51. I cannot see that the same arguments would not apply to an amaro-based JSX transformer.

    For my $0.02, this and #56822 (comment) are enough to make this wontfix.

    agreed that we can close for people who want to run JSX on nodejs without building process they can still use customization hooks

  26. github-actions commented on Jul 20, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 90 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  27. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions