diff --git a/CHANGELOG.md b/CHANGELOG.md
index 18503c4c02..2670f3c3db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Added
* [`no-string-refs`]: allow this.refs in > 18.3.0 ([#3807][] @henryqdineen)
+### Fixed
+* [`jsx-curly-brace-presence`]: avoid autofixing attributes with double quotes to a double quoted attribute ([#3814][] @ljharb)
+
+[#3814]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3814
[#3807]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3807
## [7.35.1] - 2024.09.02
diff --git a/lib/rules/jsx-curly-brace-presence.js b/lib/rules/jsx-curly-brace-presence.js
index cfbe3bda8d..0f17143ffa 100755
--- a/lib/rules/jsx-curly-brace-presence.js
+++ b/lib/rules/jsx-curly-brace-presence.js
@@ -199,10 +199,14 @@ module.exports = {
const parentType = JSXExpressionNode.parent.type;
if (parentType === 'JSXAttribute') {
- textToReplace = `"${expressionType === 'TemplateLiteral'
- ? expression.quasis[0].value.raw
- : expression.raw.slice(1, -1)
- }"`;
+ if (expressionType !== 'TemplateLiteral' && /["]/.test(expression.raw.slice(1, -1))) {
+ textToReplace = expression.raw;
+ } else {
+ textToReplace = `"${expressionType === 'TemplateLiteral'
+ ? expression.quasis[0].value.raw
+ : expression.raw.slice(1, -1)
+ }"`;
+ }
} else if (jsxUtil.isJSX(expression)) {
textToReplace = getText(context, expression);
} else {
diff --git a/tests/lib/rules/jsx-curly-brace-presence.js b/tests/lib/rules/jsx-curly-brace-presence.js
index 70fd884030..59d756737b 100755
--- a/tests/lib/rules/jsx-curly-brace-presence.js
+++ b/tests/lib/rules/jsx-curly-brace-presence.js
@@ -945,5 +945,15 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
output: ``,
errors: [{ messageId: 'unnecessaryCurly' }],
},
+ {
+ code: `
+
+ `,
+ options: ['never'],
+ output: `
+
+ `,
+ errors: [{ messageId: 'unnecessaryCurly' }],
+ }
)),
});