Skip to content

[compiler] Fix JSX tags prefixed with _ or $ incorrectly treated as host elements#36688

Open
sleitor wants to merge 1 commit into
facebook:mainfrom
sleitor:fix-36601
Open

[compiler] Fix JSX tags prefixed with _ or $ incorrectly treated as host elements#36688
sleitor wants to merge 1 commit into
facebook:mainfrom
sleitor:fix-36601

Conversation

@sleitor
Copy link
Copy Markdown
Contributor

@sleitor sleitor commented Jun 4, 2026

Summary

Fixes #36601

Problem

In lowerJsxElementName (BuildHIR.ts), the condition if (tag.match(/^[A-Z]/)) only treats JSX tags starting with an uppercase letter as component references. Tags starting with _, $, or any other non-letter character fell through to the else branch and were incorrectly classified as BuiltinTag (host/intrinsic elements).

This meant that <_Bar /> or <$Foo /> was treated like <div />, causing the compiler to skip memoization of the component and potentially producing incorrect output.

Root Cause

JSX semantics (as implemented by Babel's JSX transform) are:

  • Tag starts with lowercase letter → host/intrinsic element (string tag)
  • Everything else → component reference (in-scope identifier)

The original code only handled the first half of that rule ("starts with uppercase → component") while ignoring identifiers like _Bar and $Foo.

Fix

Change:

if (tag.match(/^[A-Z]/)) {

To:

if (!tag.match(/^[a-z]/)) {

This correctly classifies any JSX identifier that does NOT start with a lowercase letter as a component reference, matching JSX spec semantics.

Test

Added fixture jsx-underscore-prefix-component that renders <_Bar /> and verifies the compiler correctly memoizes it as a component reference.

How did you test this change?

  • Added new compiler fixture test: jsx-underscore-prefix-component
  • Ran yarn workspace babel-plugin-react-compiler lint
  • Ran snap tests with the new fixture to generate expected output ✅

…, `_Bar`, `$Foo`) treated as host elements

Previously, `lowerJsxElementName` only checked `if (tag.match(/^[A-Z]/)))`
to identify component references.  This means tags starting with `_`, `$`,
a digit, or any character other than an ASCII letter were incorrectly
treated as BuiltinTag (host/intrinsic elements) rather than component
references.

JSX semantics mirror Babel's JSX transform: a tag is a host element if
and only if it starts with a **lowercase** letter (a-z).  Everything
else (uppercase, `_`, `$`, etc.) refers to an in-scope identifier and
should be loaded as a component.

Fix: change the guard from `tag.match(/^[A-Z]/)` to
`!tag.match(/^[a-z]/)`, which correctly classifies identifiers like
`_Bar` and `$Foo` as component references.

Fixes facebook#36601
@meta-cla meta-cla Bot added the CLA Signed label Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Compiler Bug]: Components prefixed with _ are assumed host components

1 participant