perf(studio): request the player chunk before the shell's first layout

The dynamic import of @hyperframes/player ran inside the preview's mount
effect, which React schedules after the shell's first layout. On a cold
open that layout stalls the main thread for seconds, so the chunk request
waited behind it for no reason. The import is now kicked at module scope,
behind a typeof window guard that preserves the documented SSR contract
(the module registers a custom element at load), and the mount effect
awaits the already-in-flight promise. Verified in the built bundle: the
preload call sits at module top level, so the request goes out on bundle
evaluation.
This commit is contained in:
Miguel Angel Simon Sierra
2026-09-12 05:22:59 -04:00
parent c9b4263c7d
commit 1037456aa3
@@ -5,8 +5,10 @@ import { useMountEffect } from "../../hooks/useMountEffect";
import { applyPreviewVariablesToUrl } from "../../hooks/previewVariablesStore"; import { applyPreviewVariablesToUrl } from "../../hooks/previewVariablesStore";
import { HyperframesLoader } from "../../components/ui"; import { HyperframesLoader } from "../../components/ui";
// NOTE: importing "@hyperframes/player" registers a class extending HTMLElement // NOTE: importing "@hyperframes/player" registers a class extending HTMLElement
// at module load, which throws under SSR. Defer the import to the mount effect // at module load, which throws under SSR — hence the dynamic import behind a
// so it only runs in the browser. // `typeof window` guard. Kicking it at module scope rather than in the mount
// effect puts the chunk request in flight before the shell's first layout.
const playerModule = typeof window === "undefined" ? null : import("@hyperframes/player");
interface PlayerProps { interface PlayerProps {
projectId?: string; projectId?: string;
@@ -166,8 +168,7 @@ export const Player = forwardRef<HTMLIFrameElement, PlayerProps>(
let canceled = false; let canceled = false;
let cleanup: (() => void) | undefined; let cleanup: (() => void) | undefined;
// Dynamic import registers the custom element in the browser only. playerModule?.then(() => {
import("@hyperframes/player").then(() => {
if (canceled) return; if (canceled) return;
// Create the web component imperatively to avoid JSX custom-element typing. // Create the web component imperatively to avoid JSX custom-element typing.