[🐞] Function `ref` runs before the element is connected, has its later attributes, or has children
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- javascript, typescript, vite
Research direction
The ref call happens in createNewElement() in packages/qwik/src/core/client/vnode-diff.ts, inside the loop that applies props, via applyRef() calling value(element) (around lines 1099 and 1340). Start by reading that loop and comparing it with the v1 ref timing. A fix would defer function and signal refs until the element is inserted and its children exist, and a test should check isConnected and attributes inside the callback. The maintainer has not picked a design yet, so confirm the approach first.
Written by the indexing model from the issue text.
Description
Which component is affected?
Qwik Runtime
Describe the bug
A function ref on an element that mounts on the client runs while the element is still only half built. At that point the element:
- is not in the document (
el.isConnected === false), - does not have the attributes that come after
refin the JSX yet, - has no children yet.
const Probe = component$(() => {
const atRef = useSignal('pending');
return (
<div
ref={(el: Element) => {
atRef.value = `isConnected=${el.isConnected}, hasAttribute('data-after')=${el.hasAttribute('data-after')}, childNodes=${el.childNodes.length}`;
}}
data-after="set"
>
<p>In ref callback: {atRef.value}</p>
</div>
);
});
export default component$(() => {
const open = useSignal(false);
return (
<div>
<button onClick$={() => (open.value = true)}>Mount Probe</button>
{open.value && <Probe />}
</div>
);
});
Expected: the ref callback receives the finished element, already in the document: isConnected=true, hasAttribute('data-after')=true, with its children present. This is how React handles ref callbacks: it calls them after the element is attached.
Actual: isConnected=false, hasAttribute('data-after')=false, childNodes=0
This is not a v2 regression. v1 calls the ref at the same point. The behaviour then differs after one microtask (Promise.resolve().then(...)):
| in ref callback | one microtask later | |
|---|---|---|
v2 (rc.4 and main) |
isConnected=false |
isConnected=true |
| v1 (1.20) | isConnected=false |
isConnected=false |
In v2, createNewElement() calls applyRef() → value(element) from inside the loop that applies the element's props. That loop runs right after the element is created, before the element is inserted and before its children are created (vnode-diff.ts#L1099, and #L1340 for the var-props path). Any code that needs a usable element in the ref therefore fails. For example, el.showPopover() throws because the element is disconnected and popover is not set yet. el.focus() and layout measurements also fail.
Would it be possible to call function refs (and set signal refs) only after the element and its subtree are inserted?
Tested with vite dev and with a production build (vite build + vite preview), on 2.0.0-rc.4 and on current main (pkg.pr.new/QwikDev/qwik/@qwik.dev/core@main, 2026-11-01).
Reproduction
v2 playground: next.qwik.dev/playground
v1 playground (for comparison, same behaviour inside the callback): qwik.dev/playground
The two playgrounds use the same code. Only the import source differs (@qwik.dev/core vs @builder.io/qwik). They also show the one-microtask-later check.
Steps to reproduce
- Open the v2 playground link.
- Click "Mount Probe".
- "In ref callback" shows
isConnected=false, hasAttribute('data-after')=false, childNodes=0.
System Info
System:
OS: macOS 27.0.1
CPU: (14) arm64 Apple M4 Max
Binaries:
Node: 22.22.0
pnpm: 10.11.1
Browsers:
Chrome: 156.0.8078.74
npmPackages:
@qwik.dev/core: https://pkg.pr.new/QwikDev/qwik/@qwik.dev/core@main => 2.0.0-rc (also 2.0.0-rc.4)
@qwik.dev/router: https://pkg.pr.new/QwikDev/qwik/@qwik.dev/router@main => 2.0.0-rc (also 2.0.0-rc.4)
typescript: 5.9.3
vite: 8.3.5
Additional Information
I hit this while porting an overlay host. It renders a popover="manual" element and uses a function ref to call el.showPopover() as soon as possible. A useVisibleTask$ is not an option there, because a closed popover has no layout box and the task never runs. showPopover() throws when called inside the ref, because the element isn't connected and the popover attribute isn't set yet.
Workaround: set the popover attribute imperatively, then check el.isConnected on each requestAnimationFrame and call showPopover() once it is true.
- Dominant language
- TypeScript
- Stars
- 22.1k
- Forks
- 1.4k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 101
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from QwikDev/qwik
-
[🐞] SSG: "Qwik Router SSG was skipped" warning on every SSG build, while pages are prerenderedPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
[🐞] `server$` resolves `undefined` when the response is an HTML pagePossibly taken @thelastbackspace claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
QwikDev/qwik#9335 · 1 comment ·
Maintainers usually reply within 1 day
-
docs router V2
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
DX enhancement V2
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
QwikDev/qwik#8480 · 2 comments ·
Maintainers usually reply within 1 day
-
[🐞] View transition CSS doesn't un-capture `:root` in ChromePossibly taken @Nelmad claimed this 1 day ago. Opengood first issue
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
QwikDev/qwik#9373 · 1 comment ·
Maintainers usually reply within 1 day
Similar issues
-
area: web bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
kinqsradio/orbyn-engineering#227 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
joeldesante/TurfBuilder#258 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ncecere/open-mcp-portal#85 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 63/100
loathers/garbage-collector#2865 ·
Maintainers usually reply within 1 day
-
test-reliability
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Azure/azure-sdk-for-js#40672 · 1 reaction ·
Maintainers usually reply within 1 day