honojs/hono · error · Error

Invalid JSX tag name: ${tag}

Error message

Invalid JSX tag name: ${tag}

What it means

Hono's JSX runtime validates tag names when constructing a JSXNode: a tag must either be a function component or a string that is a valid HTML/SVG element name or a valid custom-element name. If it's a string that fails isValidTagName (e.g. contains invalid characters, is empty, or isn't a recognized element/custom element), this error is thrown.

Source

Thrown at src/jsx/base.ts:179

export type Child =
  | string
  | Promise<string>
  | number
  | JSXNode
  | null
  | undefined
  | boolean
  | Child[]
export class JSXNode implements HtmlEscaped {
  tag: string | Function
  props: Props
  key?: string
  children: Child[]
  isEscaped: true = true as const
  constructor(tag: string | Function, props: Props, children: Child[]) {
    if (typeof tag !== 'function' && !isValidTagName(tag)) {
      throw new Error(`Invalid JSX tag name: ${tag}`)
    }
    this.tag = tag
    this.props = props
    this.children = children
  }

  get type(): string | Function {
    return this.tag as string
  }

  // Added for compatibility with libraries that rely on React's internal structure
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  get ref(): any {
    return this.props.ref || null
  }

  toString(): string | Promise<string> {
    const render = () => {

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Check that the component variable is actually imported and not undefined; fix the import path/export name
  2. If you meant a custom element, rename it to include a hyphen and valid characters (e.g. `my-element`)
  3. If the tag is dynamic, validate/whitelist the string before interpolating it into JSX
  4. Capitalize component variables so JSX treats them as function references, not tag names

Example fix

// before
const tag = someInput // e.g. 'My Widget' or undefined
return <tag>hi</tag>
// after
const tag = typeof someTag === 'string' && /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(someTag) ? someTag : 'div'
return <tag>hi</tag>
Defensive patterns

Strategy: type-guard

Validate before calling

const isValidElementName = (s: string) =>
  /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(s) || /[A-Z]/.test(s)
const Tag = trustedTags.includes(name) ? name : 'div'

Type guard

const isJsxTag = (t: unknown): t is string | Function =>
  typeof t === 'function' ||
  (typeof t === 'string' && (HTMLElementTags.includes(t) || /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(t)))

Try / catch

null

Prevention

When it happens

Trigger: Rendering `<my-component>` where the string is not a valid custom-element name (must contain a hyphen with lowercase letters, like `my-element`); using a variable that is undefined/null/empty as a JSX tag (lowercase JSX tags that resolve to strings); dynamically building tag names like `<{"div" + suffix}>` producing an invalid name.

Common situations: Importing a component that resolves to undefined due to a missing/circular export and using it with a lowercase name; typos in intrinsic elements; attempting dynamic tag names with characters invalid in tag names (dots, spaces); forgetting that lowercase JSX tags referencing variables must be valid element or custom-element names.

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/bbc7639d5283026e. Report an issue: GitHub.