evanw/esbuild · error · LexerPanic
Unexpected backslash in JSX element
Error message
Unexpected backslash in JSX element
What it means
JSX quoted attributes follow XML escaping rules, not JavaScript: a backslash inside the quoted value is a literal backslash and does not escape the quote. esbuild special-cases the common mistake of pasting JSON.stringify output (e.g. content="say \"hi\"") and reports this message, suggesting "/' or a {...} JS expression.
Source
Thrown at internal/js_parser/js_parser.go:5422
xmlEscape = "'"
}
if xmlEscape != "" {
data := p.tracker.MsgData(p.lexer.PreviousBackslashQuoteInJSX,
"Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes:")
data.Location.Suggestion = xmlEscape
msg.Notes = append(msg.Notes, data)
}
// Option 2: Suggest using a JavaScript string
if stringRange := p.source.RangeOfString(previousStringWithBackslashLoc); stringRange.Len > 0 {
data := p.tracker.MsgData(stringRange,
"Consider using a JavaScript string inside {...} instead of a quoted JSX attribute:")
data.Location.Suggestion = fmt.Sprintf("{%s}", p.source.TextForRange(stringRange))
msg.Notes = append(msg.Notes, data)
}
p.log.AddMsg(msg)
panic(js_lexer.LexerPanic{})
}
// A slash here is a self-closing element
if p.lexer.Token == js_lexer.TSlash {
// Use NextInsideJSXElement() not Next() so we can parse ">>" as ">"
closeLoc := p.lexer.Loc()
p.lexer.NextInsideJSXElement()
if p.lexer.Token != js_lexer.TGreaterThan {
p.lexer.Expected(js_lexer.TGreaterThan)
}
return js_ast.Expr{Loc: loc, Data: &js_ast.EJSXElement{
TagOrNil: startTagOrNil,
Properties: properties,
CloseLoc: closeLoc,
IsTagSingleLine: isSingleLine,
}}
}
View on GitHub (pinned to f6058f8364)
Solutions
- Use XML entities (" for ", ' for ').
- Wrap the value in a JS expression: content={"say \"hi\""}.
- Use the opposite quote style for the attribute so no escaping is needed.
Example fix
// before
<Button content="say \"hi\"" />
// after
<Button content={"say \"hi\""} /> Defensive patterns
Strategy: validation
Validate before calling
import "github.com/evanw/esbuild/pkg/api"
res := api.Transform(jsx, api.TransformOptions{Loader: api.LoaderJSX})
for _, m := range res.Errors {
if strings.Contains(m.Text, "backslash in JSX") {
// a JS-style \' or \" was used inside a quoted JSX attribute
}
} Type guard
// Flag a quoted JSX attribute containing a JS-style backslash escape.
var reJsxEscapedQuote = regexp.MustCompile(`<[-A-Za-z0-9.]+[^>]*\b[-:A-Za-z][-:A-Za-z0-9]*\\s*=\\s*"[^"]*\\\\["'][^"]*"`)
func jsxAttrHasBackslash(src string) bool { return reJsxEscapedQuote.MatchString(src) } Prevention
- Never paste JSON.stringify output into a JSX attribute directly; wrap in {...}.
- Use XML entities (", ') for quotes inside JSX attributes.
- In codegen, emit JSX attributes as {...} expressions when the value may contain quotes.
When it happens
Trigger: <Button content="some so-called \"text\"" /> — a JS-style backslash escape inside a double-quoted JSX attribute. The lexer surfaces TSyntaxError with raw '\' and a previous-string-with-backslash location.
Common situations: Auto-generating JSX from JSON.stringify; pasting JS string escapes into JSX attributes; mixing JS and XML escaping mental models.
Related errors
- Expected identifier after %q in namespaced JSX name
- Unexpected "-"
- Unexpected end of file before a closing %s
- Unicode escape sequence is out of range
- Unexpected newline before "=>"
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/0b591c4056dda952.
Report an issue: GitHub.