fix: <vimeo-video> element race condition#220
Open
spuppo-mux wants to merge 9 commits intomuxinc:mainfrom
Open
fix: <vimeo-video> element race condition#220spuppo-mux wants to merge 9 commits intomuxinc:mainfrom
<vimeo-video> element race condition#220spuppo-mux wants to merge 9 commits intomuxinc:mainfrom
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
|
@spuppo-mux is attempting to deploy a commit to the Mux Team on Vercel. A member of the Team first needs to authorize it. |
Contributor
Author
|
Closes #219 |
…d loadComplete renewal to after the src check
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6de507c. Configure here.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
R-Delfino95
approved these changes
May 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #219
Problem
Sometimes when loading Vimeo Player (e.g. on our example switching TikTok → Vimeo) the Vimeo iframe
loaded with default styling instead of the configured color, because the
configvalue was not reaching the iframe URL.Reproduction: switch to TikTok then back to Vimeo on the Next.js example. The bug manifests as the default Vimeo blue instead of the configured pink. As this is a race condition you may need to switch multiple times for it to manifest.
How our example should look:


How it looks when the bug happens:
Root cause
By checking logs I saw that when the bug happened, we were getting into the
isInit === falsecase (andload()was running just one time), so the options weren't being applied.Digging further, two distinct issues were combined under the "race condition":
configsetter never triggered a reload. Settingconfigafter the element had already loaded simply mutated the private field — there was nothing to push the new value into the iframe.load()we hit theisFirstLoad && iframebranch, read the previous mount'sdata-configback into#config, and then reused the old iframe — so any new config set before re-attach was silently overwritten.What was done to address it
configsetter now callsthis.load()(debounced via the existing tick inload()so it batches with concurrentsrc/ attribute changes). A structural-equality short-circuit avoids redundant reloads when React re-renderswith an equivalent config object.
load()that distinguishes the cases of: no shadow DOM yet (first mount → build), shadow DOM survived a disconnect (remount → rebuild template so the iframe URL reflects current src/config),and shadow DOM came from SSR declarative shadow DOM (hydration → adopt the existing iframe; its URL is already correct and rebuilding would destroy the in-flight request, causing a visible stutter).
#wasDisconnectedflag set indisconnectedCallbackand cleared insideload()— this is what lets the SSR-hydration and remount paths be told apart.connectedCallbackre-triggersload()when re-attaching, since attributes don't change on re-attach and otherwise wouldn't fireattributeChangedCallback.disconnectedCallbackclears#loadRequested,#hasLoaded,#isInit, resetsloadComplete, and destroys the old Vimeo API instance.Extra changes (review feedback / edge cases)
loadComplete = new PublicPromise()to the original timing — earlier it had been moved before the tick, which (as flagged in review) could let handlers awaitingloadCompleteresolve prematurely against the previousload's promise. It now also runs after the early
returnfor missingsrc, so aconfig-set-before-srcno longer orphans an unresolved promise.destroy()'d on reload and on disconnect to prevent leaks of stale player instances and listeners.#setupApiListenersmethod.#onLoadedhandler to a class method, since it's used from two places.loadfor readability.Note
Medium Risk
Changes the
<vimeo-video>element’s load/lifecycle behavior (including iframe reuse vs rebuild and API teardown), which could affect playback initialization timing and SSR hydration behavior if edge cases weren’t covered.Overview
Fixes a race where
config/embed options could be missed by reworkingload()to debounce consistently, avoid orphaning existingloadCompletewhensrcis empty, and reload whenconfigactually changes.Adds explicit mount/unmount handling: on disconnect it resets internal load/init state and destroys the Vimeo API instance, and on reconnect it triggers a reload when the element was remounted.
Improves SSR declarative shadow DOM hydration by adopting an existing iframe (and recovering
configfromdata-config) instead of always rebuilding the iframe, while still rebuilding on true remounts so the iframe URL reflects currentsrc/config.Reviewed by Cursor Bugbot for commit 323907f. Bugbot is set up for automated code reviews on this repo. Configure here.