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

  1. Use XML entities (" for ", ' for ').
  2. Wrap the value in a JS expression: content={"say \"hi\""}.
  3. 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

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


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/0b591c4056dda952. Report an issue: GitHub.