In Cypress, why does a restored snapshot show a grey iframe box and a blank canvas?
answer
- Restoring must not re-run the page
- Frames are swapped, not copied
- Clone copies the node, not the drawing
- The grey box has a data URL inside
- No scripts means nothing redraws
basics
~20 sCypress replaces each iframe in the clone with a same-sized placeholder so restoring cannot re-issue its requests or re-run its code, and cloning a canvas copies the element but never its bitmap. Scripts are stripped, so nothing redraws either.
solid answer
~40 sBefore the cloned body is stored, Cypress walks the live page's `<iframe>` elements and swaps each one in the clone for a same-sized `<iframe>` placeholder that keeps the original's `id`, `class` and inline `style` so layout holds, but whose `src` is a `data:` URL reading "iframe placeholder for <original src>" on a grey background. If the frame's `contentWindow` is unreachable, the placeholder is dropped and the frame is simply removed. That is on purpose: restoring a snapshot must not re-issue the frame's network requests or re-run its JavaScript. A `<canvas>` survives as an element but arrives empty, because the DOM's clone semantics copy attributes and children and never the backing bitmap. And with every `<script>` stripped out, nothing redraws it — a restored snapshot is a still frame.
code
javascript · 9 linesit('shows the priority breakdown', () => {
cy.visit('/queue')
// the canvas is blank in every snapshot, so do not debug through it
cy.get('[data-cy=priority-chart]').should('be.visible')
// assert on the data the chart was drawn from - this lands in the DOM
cy.get('[data-cy=priority-chart]')
.should('have.attr', 'data-urgent-count', '3')
.and('have.attr', 'data-normal-count', '9')
})go deeper
Recognise the grey placeholder box and the empty chart for what they are — normal snapshot behaviour — rather than reporting them as bugs in the application.
Explain the mechanism: frames are swapped for sized placeholders so restoring cannot re-issue requests, and cloning an element never copies a canvas bitmap.
Show how you route around the gaps in a real investigation — moving the state you need into attributes, and asserting on the data behind a visual instead of the visual.
Decide what your components must expose in markup so that the whole team's debugging does not depend on visuals a snapshot structurally cannot hold.
## Why a snapshot cannot simply be the page A Cypress snapshot has to be safe to put back on screen. If it re-ran the page's code, restoring one while debugging a support-ticket queue would fire fresh requests, re-open sockets and mutate the state you were trying to inspect. So the capture is built to be **inert on purpose**, and everything that only exists because something is executing is lost with it. Three specific gaps account for most of what people notice. ## Iframes become placeholders Cypress does not clone the frames on the page. Before the clone is stored it walks the live document's `<iframe>` elements and, for each one, replaces the corresponding node in the clone: - The placeholder is itself an `<iframe>`, chosen because a frame is an inline **replaced element** whose box model is awkward to imitate with a `<div>`. - It carries the original's `id`, `class` and inline `style`, and is sized to the original's outer width and height, so the surrounding layout does not collapse. - Its `src` is set to a `data:` URL that renders the text `<iframe> placeholder for <original src>` on a light grey background with a thin border — that grey box is the tell. - If Cypress cannot reach the frame's `contentWindow` at all, there is no placeholder: the frame is removed from the clone outright and its space disappears. So an embedded chat widget, a payment frame or an advertising slot is never in a snapshot, and no assertion you make against a restored snapshot can tell you anything about what was inside one. ## Canvas comes back empty A `<canvas>` element clones like any other element — its attributes and its fallback children come across intact. What does **not** come across is the drawing surface. Under the DOM standard, cloning copies the node, not the bitmap a `2d` or WebGL context has painted into it, so the restored canvas is a correctly sized, correctly positioned, entirely blank rectangle. A priority-breakdown chart drawn on a canvas leaves a hole in the snapshot exactly where its most interesting evidence would be. The same reasoning explains a related gap: a **shadow root** is not cloned either unless it was created clonable, so the internals of a component that renders into shadow DOM can be missing from the copy while its host element is present. ## Anything script-driven is gone Because every `<script>` is removed from the clone, none of the page's own code exists in a restored snapshot: - A JavaScript-driven animation or a countdown has no code to advance it, so it is frozen at whatever value the markup held. - A CSS animation or transition on a restored element **starts over from its beginning** rather than resuming, because the clone is freshly inserted into the document when the snapshot is restored. - Media elements come back at their initial state; playback position is runtime state, not markup. - Event listeners attached by script are not attributes, so they are not cloned — which is why a restored snapshot does not respond to clicks. ## What survives, and what does not | Page feature | In a restored snapshot | |---|---| | Elements, text, classes, attributes | Faithful | | Typed input values and checked boxes | Preserved by the HTML clone steps | | Same-origin styling | Re-inserted from the captured rules | | `<iframe>` content | A grey placeholder, or nothing | | `<canvas>` drawing | Blank | | Shadow DOM internals | Usually absent | | Running animation or timer | Frozen or restarted | | Event listeners | Gone | ## Debugging around the gaps When the thing you need to see falls in the "not captured" column, stop asking the snapshot and move the evidence into the DOM or into an assertion: 1. Assert on the **data** the visual is derived from rather than the visual — for a queue chart, check the counts the component was handed, not the pixels it drew. 2. Have the component under test expose state you care about as an attribute or a `data-` value, so it lands in the markup and therefore in every snapshot. 3. For a frame you genuinely need to reach, work against its contents during the run, when the frame is live, rather than expecting the snapshot to hold them afterwards.
- Why does Cypress use an <iframe> as the placeholder rather than a plain <div>?A frame is an inline replaced element, and its box model is hard to reproduce with a block element. Reusing an `<iframe>` — with the original's class, inline style and outer dimensions — keeps the surrounding layout of the restored page looking the way it did.
- When does an iframe disappear from a Cypress snapshot entirely instead of becoming a placeholder?When Cypress cannot get at the frame's `contentWindow` — for example a frame whose document it has no access to. Rather than guess at its size, the capture removes that node from the clone, so the restored page has a gap where the frame used to be.
saying these in an interview costs you the question
- Expects an embedded widget to render in a snapshot
- Blames the app when a canvas restores blank
- Thinks a restored snapshot is clickable
- Assumes an animation resumes where it was captured
- Treats a snapshot as proof a frame loaded