preactjs/preact · error · Error

Component's "ref" property should be a function, or an objec

Error message

Component's "ref" property should be a function, or an object created by createRef(), but got [${typeof vnode.ref}] instead
${serializeVNode(vnode)}

${getOwnerStack(vnode)}

What it means

Thrown from options._diff when vnode.ref is defined but is neither a function, an object (the shape returned by createRef()/useRef()), nor a string ref permitted via preact-compat's $$typeof marker. Refs must be a callback or a ref object so Preact can attach the DOM node; any other primitive (number, boolean, string without compat) is rejected.

Source

Thrown at debug/src/debug.js:178

						`  let vnode = <My${getDisplayName(vnode)} />;\n\n` +
						'This usually happens when you export a JSX literal and not the component.' +
						`\n\n${getOwnerStack(vnode)}`
				);
			}

			throw new Error(
				'Invalid type passed to createElement(): ' +
					(Array.isArray(type) ? 'array' : type)
			);
		}

		if (
			vnode.ref !== undefined &&
			typeof vnode.ref != 'function' &&
			typeof vnode.ref != 'object' &&
			!('$$typeof' in vnode) // allow string refs when preact-compat is installed
		) {
			throw new Error(
				`Component's "ref" property should be a function, or an object created ` +
					`by createRef(), but got [${typeof vnode.ref}] instead\n` +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		}

		if (typeof vnode.type == 'string') {
			for (const key in vnode.props) {
				if (
					key[0] === 'o' &&
					key[1] === 'n' &&
					typeof vnode.props[key] != 'function' &&
					vnode.props[key] != null
				) {
					throw new Error(
						`Component's "${key}" property should be a function, ` +
							`but got [${typeof vnode.props[key]}] instead\n` +

View on GitHub (pinned to e881e7e838)

Solutions

  1. Replace string refs with createRef or callback refs: `const r = useRef(null); <input ref={r}/>`.
  2. If you must use string refs, ensure 'preact/compat' is aliased so the $$typeof marker is present.
  3. Audit ref assignments — `typeof ref` must be 'function' or 'object'.
  4. For forwarded refs, use forwardRef from 'preact/compat' rather than passing a raw prop named ref.

Example fix

// before (legacy string ref)
class Form extends Component {
  render() { return <input ref='email' />; }
}

// after
import { createRef } from 'preact';
class Form extends Component {
  email = createRef();
  render() { return <input ref={this.email} />; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidRef(ref) {
  if (ref == null) return true;
  if (typeof ref === 'function') return true;
  if (typeof ref === 'object' && 'current' in ref) return true; // createRef/useRef shape
  return false;
}

// before assigning
if (!isValidRef(maybeRef)) throw new Error('ref must be function or { current } object');

Type guard

/** @param {unknown} r */
function isPreactRef(r) {
  if (r == null) return false;
  return typeof r === 'function' || (typeof r === 'object' && 'current' in r);
}

Prevention

When it happens

Trigger: Passing a string ref without preact-compat installed: `<input ref='inputRef'/>`; passing a number or boolean: `ref={0}` or `ref={true}`; assigning ref to the result of an expression that yields a primitive; forwarding a ref prop that was unexpectedly a primitive.

Common situations: Copy-paste from a React class component using string refs (legacy); ref forwarding through a prop typed loosely as any; HOC that clones a child but passes through a mutated ref; version mismatch where preact-compat is no longer present (Preact 10+ ships refs as functions/objects only).

Related errors


AI-assisted analysis of preactjs/preact@e881e7e838 (2026-08-13). Data as JSON: /api/errors/473071903868c854. Report an issue: GitHub.