honojs/hono · error · Error

Can only set one of `children` or `props.dangerouslySetInner

Error message

Can only set one of `children` or `props.dangerouslySetInnerHTML`.

What it means

In hono/jsx server rendering, setting dangerouslySetInnerHTML replaces the element's children with raw HTML. To avoid ambiguous output, Hono throws if the same element also has children when serializing to a string buffer.

Source

Thrown at src/jsx/base.ts:252

        })
        buffer[0] += ' style="'
        escapeToBuffer(styleStr, buffer)
        buffer[0] += '"'
      } else if (typeof v === 'string') {
        buffer[0] += ` ${key}="`
        escapeToBuffer(v, buffer)
        buffer[0] += '"'
      } else if (v === null || v === undefined) {
        // Do nothing
      } else if (typeof v === 'number' || (v as HtmlEscaped).isEscaped) {
        buffer[0] += ` ${key}="${v}"`
      } else if (typeof v === 'boolean' && booleanAttributes.includes(key)) {
        if (v) {
          buffer[0] += ` ${key}=""`
        }
      } else if (key === 'dangerouslySetInnerHTML') {
        if (children.length > 0) {
          throw new Error('Can only set one of `children` or `props.dangerouslySetInnerHTML`.')
        }

        children = [raw(v.__html)]
      } else if (v instanceof Promise) {
        buffer[0] += ` ${key}="`
        buffer.unshift('"', v)
      } else if (typeof v === 'function') {
        if (!key.startsWith('on') && key !== 'ref') {
          throw new Error(`Invalid prop '${key}' of type 'function' supplied to '${tag}'.`)
        }
        // maybe event handler for client components, just ignore in server components
      } else {
        buffer[0] += ` ${key}="`
        escapeToBuffer(v.toString(), buffer)
        buffer[0] += '"'
      }
    }

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Remove the children from the element that uses dangerouslySetInnerHTML
  2. If you need conditional behavior, choose one branch: render children XOR set dangerouslySetInnerHTML, never both
  3. Wrap the injected HTML in a dedicated element with no children

Example fix

// before
<div dangerouslySetInnerHTML={{ __html: html }}>
  <p>Fallback</p>
</div>
// after
<div dangerouslySetInnerHTML={{ __html: html }}></div>
Defensive patterns

Strategy: validation

Validate before calling

function SafeHtml({ html, children }: { html?: string; children?: Child[] }) {
  if (html && children && children.length > 0) {
    throw new TypeError('Pass either html or children, not both')
  }
  return <div {...(html ? { dangerouslySetInnerHTML: { __html: html } } : {})}>{children}</div>
}

Type guard

const hasBoth = (p: { children?: unknown[]; dangerouslySetInnerHTML?: unknown }) =>
  Boolean(p.dangerouslySetInnerHTML && p.children && p.children.length > 0)

Try / catch

null

Prevention

When it happens

Trigger: JSX like `<div dangerouslySetInnerHTML={{ __html: rawHtml }}>{'text'}</div>` or `<div dangerouslySetInnerHTML={{__html}}><span>x</span></div>` — any element where both children and dangerouslySetInnerHTML are present at render time.

Common situations: Copy-pasting React DOM patterns that silently prefer dangerouslySetInnerHTML; adding fallback children for empty HTML then forgetting to remove them; conditionally injecting HTML via dangerouslySetInnerHTML in a component that always renders children.

Related errors


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