{"record":{"id":"473071903868c854","repo":"preactjs/preact","slug":"component-s-ref-property-should-be-a-function-o","errorCode":null,"errorMessage":"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)}","messagePattern":"Component's \"ref\" property should be a function, or an object created by createRef\\(\\), but got \\[(.+?)\\] instead\n(.+?)\n\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"debug/src/debug.js","lineNumber":178,"sourceCode":"\t\t\t\t\t\t`  let vnode = <My${getDisplayName(vnode)} />;\\n\\n` +\n\t\t\t\t\t\t'This usually happens when you export a JSX literal and not the component.' +\n\t\t\t\t\t\t`\\n\\n${getOwnerStack(vnode)}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t'Invalid type passed to createElement(): ' +\n\t\t\t\t\t(Array.isArray(type) ? 'array' : type)\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tvnode.ref !== undefined &&\n\t\t\ttypeof vnode.ref != 'function' &&\n\t\t\ttypeof vnode.ref != 'object' &&\n\t\t\t!('$$typeof' in vnode) // allow string refs when preact-compat is installed\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Component's \"ref\" property should be a function, or an object created ` +\n\t\t\t\t\t`by createRef(), but got [${typeof vnode.ref}] instead\\n` +\n\t\t\t\t\tserializeVNode(vnode) +\n\t\t\t\t\t`\\n\\n${getOwnerStack(vnode)}`\n\t\t\t);\n\t\t}\n\n\t\tif (typeof vnode.type == 'string') {\n\t\t\tfor (const key in vnode.props) {\n\t\t\t\tif (\n\t\t\t\t\tkey[0] === 'o' &&\n\t\t\t\t\tkey[1] === 'n' &&\n\t\t\t\t\ttypeof vnode.props[key] != 'function' &&\n\t\t\t\t\tvnode.props[key] != null\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Component's \"${key}\" property should be a function, ` +\n\t\t\t\t\t\t\t`but got [${typeof vnode.props[key]}] instead\\n` +","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/preactjs/preact/blob/e881e7e8386d7a430f87498a41c6a532992b4599/debug/src/debug.js#L160-L196","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Replace string refs with createRef or callback refs: `const r = useRef(null); <input ref={r}/>`.","If you must use string refs, ensure 'preact/compat' is aliased so the $$typeof marker is present.","Audit ref assignments — `typeof ref` must be 'function' or 'object'.","For forwarded refs, use forwardRef from 'preact/compat' rather than passing a raw prop named ref."],"exampleFix":"// before (legacy string ref)\nclass Form extends Component {\n  render() { return <input ref='email' />; }\n}\n\n// after\nimport { createRef } from 'preact';\nclass Form extends Component {\n  email = createRef();\n  render() { return <input ref={this.email} />; }\n}","handlingStrategy":"type-guard","validationCode":"function isValidRef(ref) {\n  if (ref == null) return true;\n  if (typeof ref === 'function') return true;\n  if (typeof ref === 'object' && 'current' in ref) return true; // createRef/useRef shape\n  return false;\n}\n\n// before assigning\nif (!isValidRef(maybeRef)) throw new Error('ref must be function or { current } object');","typeGuard":"/** @param {unknown} r */\nfunction isPreactRef(r) {\n  if (r == null) return false;\n  return typeof r === 'function' || (typeof r === 'object' && 'current' in r);\n}","tryCatchPattern":null,"preventionTips":["Use createRef()/useRef() exclusively — never string refs unless preact/compat is aliased.","Type ref props as `Ref<T> | undefined` in TS so non-conforming values are caught at compile time.","When forwarding refs through HOCs use forwardRef from 'preact/compat'."],"tags":["preact","ref","createref","compat","debug-build"],"backgroundTag":null,"analyzedSha":"e881e7e8386d7a430f87498a41c6a532992b4599","analyzedAt":"2026-08-13T04:27:04.532Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}