skip to content

What does the srcdoc attribute on an HTML <iframe> do, and how does it interact with the src attribute?

level: juniorimportance: nice to knowfreq 24%

answer

  1. the document travels in the attribute
  2. one of the two attributes wins
  3. quotes and ampersands must be escaped
  4. no URL, so no origin of its own
  5. visual isolation is not security isolation

basics

~20 s

srcdoc holds the framed page's HTML inline in the attribute instead of fetching it from a URL. When srcdoc is present it wins over src, which is left as a fallback, and the resulting document inherits the embedding page's origin unless you also sandbox it.

solid answer

~40 s

`srcdoc` lets you write the frame's entire document inline: `<iframe srcdoc="<p>Hello</p>"></iframe>`. No network request is made for the frame's content. If both attributes are present, `srcdoc` takes precedence and `src` acts as a fallback for anything that does not support it. Because the value is attribute text, any `"` and `&` inside it must be escaped as `&quot;` and `&amp;`, which is why generated `srcdoc` markup looks noisy. The important consequence is the origin: a `srcdoc` document inherits the origin of the embedding page rather than getting one of its own, so its scripts are same-origin with your page and can touch your DOM, cookies and storage. Inlining untrusted HTML into `srcdoc` is therefore no safer than inlining it directly — add `sandbox` if the content is not yours.

code

html · 9 lines
html
<!-- srcdoc wins over src; the frame inherits the page's origin -->
<iframe srcdoc="<!doctype html><p>Inline &amp; escaped</p>"
        src="/fallback.html"
        title="Trusted inline preview"></iframe>

<!-- untrusted markup: sandbox replaces the inherited origin -->
<iframe sandbox
        srcdoc="&lt;p onclick=&quot;alert(1)&quot;&gt;untrusted&lt;/p&gt;"
        title="Untrusted preview"></iframe>

go deeper

for a junior

Know that srcdoc carries the frame's HTML inline instead of a URL, that it takes precedence when src is also present, and that quotes and ampersands inside the value have to be escaped.

for a middle

Explain the origin inheritance: a srcdoc document reports about:srcdoc and takes the embedder's origin, so its scripts are same-origin with your page unless you add sandbox. Say why that makes visual isolation and security isolation different things.

for a senior

Show the judgment call — srcdoc for markup you already hold and want rendered in a clean styling context, src when a server owns the content and separate caching matters — plus the sandbox configuration you would pair with untrusted inline markup.

for a principal

Own where user-authored HTML gets rendered across the product at all: whether previews live in a sandboxed frame on a separate origin, who owns the escaping in the templating layer, and how that decision is enforced rather than rediscovered per feature.

## What the attribute does `<iframe src>` names a URL for the browser to fetch and display in the frame. `<iframe srcdoc>` skips the fetch entirely: the attribute's *value* is the HTML source of the framed document. ```html <iframe srcdoc="<!doctype html><p>Rendered inside the frame.</p>" title="Inline preview"></iframe> ``` The browser parses that string as a complete document — you may include a doctype, `<style>`, `<script>`, whatever you like — and the frame gets a real, separate document with its own DOM, its own stylesheets and its own layout viewport. That isolation of *styles and layout* is the usual reason people reach for it: an email preview or a user-authored snippet renders without your page's CSS leaking in. ## Precedence and fallback When both attributes appear, `srcdoc` wins: ```html <iframe srcdoc="<p>Inline content</p>" src="/fallback.html" title="Preview"></iframe> ``` The `src` is a fallback path for a user agent that does not implement `srcdoc`. In current browsers you will always see the inline content. It is a harmless belt-and-braces pattern, not a way to show both. ## Escaping — the practical trap The value is an HTML attribute, so it plays by attribute rules. Inside a double-quoted attribute you must escape `"` as `&quot;` and `&` as `&amp;`; if you quote the attribute with single quotes, `'` becomes `&#39;` instead. Any markup with attributes of its own quickly turns unreadable: ```html <iframe srcdoc="&lt;a href=&quot;https://example.com&quot;&gt;Link&lt;/a&gt;" title="Escaped example"></iframe> ``` This is why `srcdoc` is usually *generated* rather than hand-written, and why a build step or template that forgets to escape produces a frame whose markup breaks out of the attribute and lands in the parent document — a markup bug with real consequences when the content came from a user. ## The origin, and why it matters A `srcdoc` document has no URL of its own to derive an origin from; it reports `about:srcdoc` and **inherits the origin of the embedding page**. That single fact drives all the security advice around the attribute: - Script inside `srcdoc` is same-origin with your page. It can read and write `window.parent.document`, your cookies, and your `localStorage`. - So a frame is *not* a security boundary here. Putting untrusted HTML in `srcdoc` is equivalent to injecting it into your own page — the visual isolation misleads people into thinking otherwise. - The fix is the `sandbox` attribute, which replaces the inherited origin with a unique opaque one: ```html <iframe sandbox srcdoc="&lt;p&gt;untrusted&lt;/p&gt;" title="Untrusted preview"></iframe> ``` With a bare `sandbox`, scripts do not run and the document has no access to anything of yours. If the preview genuinely needs to run its own script, `sandbox="allow-scripts"` — deliberately without `allow-same-origin` — keeps the opaque origin while letting the code execute. ## When to choose which Reach for `src` when the content is a real page that a server owns: a third-party widget, a video embed, a document viewer. Reach for `srcdoc` when you already hold the markup in memory and want it rendered in an isolated styling context without a round trip — email previews, template previews, code-sandbox output panes, rendering a chunk of stored HTML that must not inherit your CSS. Relative URLs inside `srcdoc` content resolve against the embedding document's base URL, which is convenient when the inline markup references your own assets and surprising if you expected the frame to have its own base. ## Details worth stating in an interview Give `srcdoc` frames a `title` like any other frame — assistive technology announces frames by that string. And keep the size honest: the whole document travels in an attribute inside your HTML response, so a large `srcdoc` inflates the parent page's bytes and cannot be cached separately the way a `src` document can. That tradeoff — no extra request, but no separate caching either — is the one-line summary of when the attribute earns its place.

  • If a page puts user-submitted HTML into srcdoc, what has it actually achieved?
    Style and layout isolation only. The `srcdoc` document inherits the embedder's origin, so any script in that markup is same-origin with the page and can reach its DOM, cookies and storage — the same exposure as injecting the HTML directly. Adding `sandbox` (with `allow-scripts` at most, never `allow-same-origin`) is what turns it into a real boundary.
  • How do relative URLs inside srcdoc content resolve?
    Against the embedding document's base URL, since the frame has no URL of its own — it reports `about:srcdoc`. So `<img src="/logo.png">` inside the attribute points at your site's asset, which is usually convenient but surprises people who expected the frame to carry its own base.
  • What do you give up by using srcdoc instead of src?
    Separate caching and a separate response. A `src` document is fetched and cached on its own terms; a `srcdoc` document is bytes inside the parent's HTML, so it re-ships with every page load and inflates that response. In exchange you avoid one request and can render markup you already hold in memory.

saying these in an interview costs you the question

  • Thinks src and srcdoc render together when both are present
  • Believes a srcdoc frame is a security boundary by itself
  • Forgets that quotes inside the value must be escaped
  • Assumes the frame gets its own origin because it is a frame
  • Expects relative URLs inside srcdoc to resolve against the frame

context