<iframe> and <embed> : The Embedded Content Elements
The <iframe> and <embed> elements allow embedding external content into PDF documents. They support loading remote HTML files that are dynamically parsed and embedded at render time. These elements enable content reuse, modular document composition, and dynamic content inclusion.
On this page
- Usage
- Supported Attributes
- Notes
- Examples
- Basic Content Inclusion
- Modular Document Composition
- Dynamic Content Loading
- Dynamic Content with data-content
- Styled Iframe with Dimensions
- Iframe with Passthrough Styling
- Conditional Content Loading
- Loading Remote Content
- Multi-Language Support
- Nested Iframes
- Template Variations
- Iframe with Fallback Content
- Reusable Components
- Dynamic Report Sections
- Themed Document Assembly
- Page-Specific Headers/Footers
- Email Template Embedding
- Form Components
- API-Driven Content
- Responsive Container Sizing
- Conditional Regional Content
- Complex Document Structure
- See Also
Usage
The <iframe> and <embed> elements enable embedded content that:
- Load and parse external HTML documents via the
srcattribute - Support remote content loading from URLs or local file paths
- Parse and render external content within the current document context
- Support styling through CSS and inline styles
- Allow dynamic content sources through data binding
- Support visibility control and conditional rendering
- Enable document composition from multiple sources
- Can be nested within any container element
- Support passthrough styling mode for iframe content
<!-- Basic iframe loading external HTML -->
<iframe src="header.html"></iframe>
<!-- Embed element with external content -->
<embed src="footer.html" />
<!-- Styled iframe with dimensions -->
<iframe src="content.html" style="width: 100%; min-height: 400pt;"></iframe>
Supported Attributes
Standard HTML Attributes
| Attribute | Type | Description |
|---|---|---|
id |
string | Unique identifier for the element. |
class |
string | CSS class name(s) for styling. Multiple classes separated by spaces. |
style |
string | Inline CSS styles applied directly to the element. |
title |
string | Sets the outline/bookmark title for the embedded content. |
hidden |
string | Controls visibility. Set to “hidden” to hide the element, or omit/empty to show. |
Embedded Content Attributes
| Attribute | Type | Description |
|---|---|---|
src |
string | Source URL or file path for the external content to load and parse. Mutually exclusive with data-content in practice — if both are used, src loads first and data-content binds additional content afterwards. |
allow |
string | (iframe only) Content permissions policy controlling what is kept from the embedded content and what passes through from the parent. See Content Permissions below. |
data-content |
string | Dynamically bound content, parsed and inserted the same way as src-loaded content, including full allow policy enforcement. See data-content attribute. |
data-content-type |
string | MIME type used to parse data-content (e.g. text/html, application/xhtml+xml, text/markdown). Defaults to the document’s default content type. |
data-content-action |
string | append (default), prepend, or replace — how data-content is inserted relative to existing children. |
data-passthrough |
boolean | Legacy, obsolete. Sets both data-passthrough and style-passthrough in allow together. Retained for templates written before allow existed. See the migration note. |
CSS Style Support
Both <iframe> and <embed> elements support extensive CSS styling:
Sizing:
width,height,min-width,max-width,min-height,max-height
Positioning:
display:block(default),inline,inline-block,noneposition:static,relative,absolutefloat:left,right,noneclear:both,left,right,nonetop,left,right,bottom(for positioned elements)
Spacing:
margin,margin-top,margin-right,margin-bottom,margin-leftpadding(all variants)
Visual Effects:
border,border-width,border-color,border-style,border-radiusbackground,background-coloropacity
Notes
How Embedded Content Works in PDF
Unlike web browsers where iframes create separate browsing contexts, Scryber’s <iframe> and <embed> elements work as content inclusion mechanisms:
- Parse-Time Loading: External content is fetched and parsed during document generation
- Content Merging: The parsed content becomes part of the parent document’s content tree
- No Sandboxing: Embedded content shares the same PDF document context
- Content Permissions: Styles, images, links, navigation, and data can be controlled via the
allowattribute (iframe only) - Static Inclusion: Content is resolved at generation time, not at viewing time
This approach is similar to server-side includes (SSI) or template partials rather than browser iframe behavior.
iframe vs embed
Both elements function similarly with one key difference:
iframe:
- Extends the
Divcomponent - Supports the
allowattribute for a ten-type content permissions policy (styles, images, links, navigation, nested frames, forms, data, outer HTML) - By default, isolates most embedded content (data, parent styles,
<style>/<link>tags, outer HTML, nested frames, forms all denied) while keeping inline styles, images, and navigation - Can contain fallback content in its body
- Best for complete HTML documents or sections, especially untrusted or third-party content
data-contentbinding runs through the same permission-cleaning pipeline assrc
embed:
- Extends
VisualComponentdirectly - Always inherits parent styles and data; no
allowsupport, no content cleaning - Simpler component model
- Typically used for smaller, trusted content fragments
- Best for reusable snippets and partials from your own templates
External Content Sources
The src attribute supports multiple source types:
- Relative File Paths:
<iframe src="partials/header.html"></iframe> <embed src="../shared/footer.html" /> - Absolute File Paths:
<iframe src="/templates/navigation.html"></iframe> - Remote URLs:
<iframe src="https://example.com/content.html"></iframe> <embed src="https://api.example.com/template?id=123" /> -
Dynamic Sources via Data Binding:
<iframe src="{{model.contentUrl}}"></iframe>
Supported Content Types
The embedded content source should be valid HTML that Scryber can parse:
- HTML Documents: Complete HTML with
<html>,<head>, and<body>tags - HTML Fragments: Partial HTML containing just body content
- XML Documents: Well-formed XML that follows Scryber’s namespace conventions
- PDF Templates: Other PDF template files in HTML format
Content must be parseable by Scryber’s HTML parser. Standard HTML5 elements and Scryber-specific components are supported.
Content Permissions (allow)
The allow attribute controls what is kept from the embedded content and what passes through from the parent document. It replaces the older data-passthrough boolean with ten independent permission types, each set to any (allowed) or none (denied):
| Type key | Governs | Default |
|---|---|---|
data-passthrough |
Parent’s data-binding stack (model, params) visible to embedded content |
none |
style-passthrough |
Parent document’s CSS styles apply to embedded elements | none |
inner-style |
<style> blocks inside the embedded content are kept |
none |
inner-link |
<link rel="stylesheet"> elements inside the embedded content are kept |
none |
inner-navigation |
<a href> targets inside the embedded content are kept |
any |
inner-images |
<img> elements inside the embedded content are kept |
any |
outer-html |
Full <html>/<body> structure preserved vs. reduced to body content |
none |
inline-styles |
style="..." attributes on embedded elements are kept |
any |
inner-frames |
Nested <iframe>/<embed>/<object> elements are kept |
none |
inner-forms |
<form>, <input>, <select>, <button> elements are kept |
none |
<!-- Default policy (no allow attribute): equivalent to -->
<iframe src="content.html"
allow="inline-styles any; inner-images any; inner-navigation any"></iframe>
<!-- Inherit parent styles instead of isolating -->
<iframe src="content.html" allow="style-passthrough any"></iframe>
<!-- Fully trusted internal content -->
<iframe src="internal.html"
allow="data-passthrough any; style-passthrough any; inner-style any;
inner-link any; outer-html any; inner-frames any; inner-forms any"></iframe>
Every permission is enforced identically whether the content is loaded via src or bound dynamically via data-content — both paths run through the same content-cleaning step after parsing.
Full grammar, per-type defaults, and further examples: allow attribute reference.
Remote Content Loading
When using remote URLs, consider:
- Network Access: Scryber must have network access to fetch remote content
- Performance: Remote content loading adds network latency to document generation
- Caching: Consider implementing caching mechanisms for frequently used remote content
- Error Handling: Use proper error handling for failed remote requests
- Security: Validate and trust remote content sources
Error Handling
When external content fails to load:
- In Strict conformance mode: An error is thrown, stopping document generation
- In Lax conformance mode: The error is logged and generation continues
- Use try-catch blocks or conformance settings to handle loading failures gracefully
Content Security
When embedding external content:
- Trust Content Sources: Only embed content from trusted sources
- Validate URLs: Sanitize and validate dynamic URL sources
- Input Validation: Validate any user-provided source paths
- Local File Access: Be cautious with local file system access in web applications
Fallback Content
The <iframe> element can contain fallback content displayed if loading fails:
<iframe src="content.html">
<p>This content will be shown if content.html fails to load</p>
</iframe>
Note: With <embed> being a self-closing element, fallback content should be handled externally.
Class Hierarchy
In the Scryber codebase:
HTMLiFrame:
- Extends
Div→Panel→ContainerComponent→VisualComponent - Decorated with
[PDFRemoteParsableComponent("iframe", SourceAttribute = "src")] - Supports
allow(AllowPolicy/DocumentPermissionsPolicy) for the ten-type content permissions policy - Overrides content binding so
data-contentenforces the same policy assrc - Default display mode:
block
HTMLEmbed:
- Extends
VisualComponent→Component - Decorated with
[PDFRemoteParsableComponent("embed", SourceAttribute = "src")] - Implements
IInvisibleContainerinterface - Simpler component model without style isolation
Examples
Basic Content Inclusion
<!-- Load header from external file -->
<iframe src="templates/header.html"></iframe>
<div class="content">
<h1>Main Document Content</h1>
<p>This is the main document content...</p>
</div>
<!-- Load footer from external file -->
<embed src="templates/footer.html" />
Modular Document Composition
<!DOCTYPE html>
<html>
<head>
<title>Modular Report</title>
<style>
body { font-family: Arial, sans-serif; }
.section { margin: 20pt 0; }
</style>
</head>
<body>
<!-- Company header -->
<iframe src="partials/company-header.html"></iframe>
<!-- Executive summary -->
<div class="section">
<iframe src="reports/executive-summary.html"></iframe>
</div>
<!-- Financial data -->
<div class="section">
<iframe src="reports/financial-data.html"></iframe>
</div>
<!-- Charts and graphs -->
<div class="section">
<iframe src="reports/charts.html"></iframe>
</div>
<!-- Legal footer -->
<embed src="partials/legal-footer.html" />
</body>
</html>
Dynamic Content Loading
<!-- With model = { headerTemplate: "header-v2.html", footerTemplate: "footer-standard.html" } -->
<iframe src="{{model.headerTemplate}}"></iframe>
<div class="main-content">
<h1>{{model.title}}</h1>
<p>{{model.description}}</p>
</div>
<iframe src="{{model.footerTemplate}}"></iframe>
Dynamic Content with data-content
Bind markup directly instead of loading it from a source path — the same allow policy is enforced on the bound content:
<!-- Model: { customer: { name: "Acme Corp" }, sectionHtml: "<h2>Notes</h2><p>Follow up next week.</p>" } -->
<iframe data-content="{{model.sectionHtml}}"
allow="data-passthrough any; inner-images any"></iframe>
<!-- data-passthrough lets the bound content see the parent's model -->
<iframe data-content="<div>For {{model.customer.name}}</div>"
allow="data-passthrough any"></iframe>
See the data-content attribute reference for data-content-type (HTML, XHTML, Markdown) and data-content-action (append/prepend/replace).
Styled Iframe with Dimensions
<style>
.embedded-content {
width: 100%;
min-height: 300pt;
border: 1pt solid #ccc;
padding: 15pt;
background-color: #f9f9f9;
}
</style>
<iframe src="content/article.html" class="embedded-content"></iframe>
Iframe with Passthrough Styling
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: 'Helvetica', sans-serif;
color: #333;
font-size: 11pt;
}
h1 { color: #336699; }
h2 { color: #5588bb; }
</style>
</head>
<body>
<!-- Content inherits parent styles -->
<iframe src="section1.html" allow="style-passthrough any"></iframe>
<!-- Content uses its own styles only (explicit) -->
<iframe src="section2.html" allow="style-passthrough none"></iframe>
<!-- Default behavior (style-passthrough denied by default) -->
<iframe src="section3.html"></iframe>
</body>
</html>
Conditional Content Loading
<!-- With model = { showHeader: true, showFooter: false } -->
<iframe src="header.html" hidden="{{model.showHeader ? '' : 'hidden'}}"></iframe>
<div class="content">
<h1>Document Content</h1>
</div>
<iframe src="footer.html" hidden="{{model.showFooter ? '' : 'hidden'}}"></iframe>
Loading Remote Content
<!-- Load from remote API -->
<iframe src="https://api.example.com/templates/header?format=html"></iframe>
<!-- Load from CDN -->
<embed src="https://cdn.example.com/shared/footer.html" />
<!-- Load from internal network -->
<iframe src="http://internal.company.com/templates/disclaimer.html"></iframe>
Multi-Language Support
<!-- With model = { language: "en", region: "US" } -->
<iframe src="i18n/{{model.language}}/header.html"></iframe>
<div class="content">
<iframe src="i18n/{{model.language}}/{{model.region}}/content.html"></iframe>
</div>
<iframe src="i18n/{{model.language}}/footer.html"></iframe>
Nested Iframes
<!-- main-document.html -->
<div>
<h1>Main Document</h1>
<iframe src="section-with-subsections.html"></iframe>
</div>
<!-- section-with-subsections.html -->
<div class="section">
<h2>Section Title</h2>
<iframe src="subsection-a.html"></iframe>
<iframe src="subsection-b.html"></iframe>
</div>
Template Variations
<!-- With model = { templateVersion: 2, customerType: "premium" } -->
<iframe src="headers/header-v{{model.templateVersion}}.html"></iframe>
<div class="main-content">
<!-- Customer-specific content -->
<iframe src="content/{{model.customerType}}/main.html"></iframe>
</div>
<iframe src="footers/footer-{{model.customerType}}.html"></iframe>
Iframe with Fallback Content
<iframe src="remote-content.html">
<!-- Fallback content if loading fails -->
<div style="padding: 20pt; background-color: #fff3cd; border: 1pt solid #ffc107;">
<h3 style="margin-top: 0;">Content Unavailable</h3>
<p>The remote content could not be loaded. This is fallback content.</p>
</div>
</iframe>
Reusable Components
<!-- Load reusable table header -->
<table style="width: 100%;">
<thead>
<tr>
<embed src="components/table-header.html" />
</tr>
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td>Data 2</td>
<td>Data 3</td>
</tr>
</tbody>
</table>
Dynamic Report Sections
<!-- With model.sections = [{src: "intro.html"}, {src: "analysis.html"}, {src: "conclusion.html"}] -->
<div class="report">
<h1>{{model.title}}</h1>
<template data-bind="{{model.sections}}">
<div class="report-section">
<iframe src="sections/{{.src}}" style="width: 100%; margin: 15pt 0;"></iframe>
</div>
</template>
</div>
Themed Document Assembly
<!DOCTYPE html>
<html>
<head>
<style>
/* Global theme styles */
body {
font-family: 'Georgia', serif;
color: #2c3e50;
line-height: 1.6;
}
.theme-primary { color: #3498db; }
.theme-secondary { color: #2ecc71; }
</style>
</head>
<body>
<!-- All iframes with passthrough will inherit theme -->
<iframe src="sections/cover.html" allow="style-passthrough any"></iframe>
<iframe src="sections/toc.html" allow="style-passthrough any"></iframe>
<iframe src="sections/chapter1.html" allow="style-passthrough any"></iframe>
<iframe src="sections/chapter2.html" allow="style-passthrough any"></iframe>
</body>
</html>
Page-Specific Headers/Footers
<!DOCTYPE html>
<html>
<head>
<title>Multi-Section Report</title>
</head>
<body>
<!-- Section 1 with specific header -->
<div style="page-break-after: always;">
<iframe src="headers/section1-header.html"></iframe>
<div class="content">Section 1 content...</div>
</div>
<!-- Section 2 with different header -->
<div style="page-break-after: always;">
<iframe src="headers/section2-header.html"></iframe>
<div class="content">Section 2 content...</div>
</div>
<!-- Section 3 with another header -->
<div>
<iframe src="headers/section3-header.html"></iframe>
<div class="content">Section 3 content...</div>
</div>
</body>
</html>
Email Template Embedding
<!-- Load email signature from shared template -->
<div class="email-body">
<p>Dear {{model.recipientName}},</p>
<p>{{model.messageBody}}</p>
<iframe src="templates/signatures/{{model.senderDepartment}}.html"></iframe>
</div>
Form Components
<div class="application-form">
<h1>Application Form</h1>
<!-- Applicant information section -->
<iframe src="form-sections/applicant-info.html"></iframe>
<!-- Employment history section -->
<iframe src="form-sections/employment-history.html"></iframe>
<!-- References section -->
<iframe src="form-sections/references.html"></iframe>
<!-- Legal disclaimers -->
<embed src="form-sections/legal-disclaimers.html" />
</div>
API-Driven Content
<!-- With model = { apiEndpoint: "https://api.example.com", reportId: "12345" } -->
<div class="report">
<!-- Load report header from API -->
<iframe src="{{model.apiEndpoint}}/reports/{{model.reportId}}/header.html"></iframe>
<!-- Load report body from API -->
<iframe src="{{model.apiEndpoint}}/reports/{{model.reportId}}/body.html"
style="min-height: 500pt;"></iframe>
<!-- Load report footer from API -->
<iframe src="{{model.apiEndpoint}}/reports/{{model.reportId}}/footer.html"></iframe>
</div>
Responsive Container Sizing
<style>
.responsive-container {
width: 100%;
min-height: 200pt;
max-height: 600pt;
overflow: hidden;
}
</style>
<div class="responsive-container">
<iframe src="flexible-content.html" style="width: 100%; height: auto;"></iframe>
</div>
Conditional Regional Content
<!-- With model = { country: "US", state: "CA" } -->
<div class="regional-document">
<!-- Country-specific header -->
<iframe src="content/{{model.country}}/header.html"></iframe>
<!-- State/province-specific content -->
<iframe src="content/{{model.country}}/{{model.state}}/main.html"></iframe>
<!-- Country-specific footer with legal info -->
<iframe src="content/{{model.country}}/legal-footer.html"></iframe>
</div>
Complex Document Structure
<!DOCTYPE html>
<html>
<head>
<title>Annual Report</title>
</head>
<body>
<!-- Cover page -->
<div style="page-break-after: always;">
<iframe src="report/cover.html"></iframe>
</div>
<!-- Table of contents -->
<div style="page-break-after: always;">
<iframe src="report/toc.html"></iframe>
</div>
<!-- Executive summary -->
<div style="page-break-after: always;">
<iframe src="report/executive-summary.html" allow="style-passthrough any"></iframe>
</div>
<!-- Financial statements -->
<div style="page-break-after: always;">
<h1>Financial Statements</h1>
<iframe src="report/balance-sheet.html"></iframe>
<iframe src="report/income-statement.html"></iframe>
<iframe src="report/cash-flow.html"></iframe>
</div>
<!-- Notes to financial statements -->
<div style="page-break-after: always;">
<iframe src="report/financial-notes.html"></iframe>
</div>
<!-- Management discussion -->
<div style="page-break-after: always;">
<iframe src="report/management-discussion.html"></iframe>
</div>
<!-- Appendices -->
<div>
<h1>Appendices</h1>
<iframe src="report/appendix-a.html"></iframe>
<iframe src="report/appendix-b.html"></iframe>
<iframe src="report/appendix-c.html"></iframe>
</div>
</body>
</html>
See Also
- allow attribute - Full content permissions policy reference
- data-content attribute - Dynamic content binding, including markdown transform
- object - Object element for file attachments
- picture - Picture element for responsive images
- img - Image element
- div - Container element
- template - Template element for data binding
- Data Binding - Data binding and expressions
- Document Parser - HTML/XML parsing in Scryber
- Remote Resources - Loading remote content