How to Fit a PDF to an HTML Iframe
Make an embedded PDF responsive, choose the right height, and get reliable fit-to-width behavior with native iframe or PDF.js.
Short answer: make the iframe fill its container with width: 100%, give it an intentional height, and provide a direct PDF link. The browser’s built-in PDF viewer controls how pages are zoomed inside that box. CSS such as object-fit cannot scale an iframe’s PDF content.
1. Responsive PDF iframe (native browser viewer)
Use a containing block and a deliberate viewport-based height:
<div class="pdf-frame">
<iframe
src="/files/guide.pdf"
title="Product guide PDF"
loading="lazy"></iframe>
</div>
<p><a href="/files/guide.pdf">Open or download the PDF</a></p>
.pdf-frame {
width: 100%;
min-height: 70vh;
}
.pdf-frame iframe {
display: block;
width: 100%;
height: 70vh;
min-height: 32rem;
border: 0;
}
width: 100% makes the iframe box follow its containing block. The height is a design decision: a native viewer does not expand the iframe to the total length of a multi-page PDF. Adjust 70vh and 32rem for your layout, or use a fixed height when the surrounding design requires one. MDN documents the iframe sizing and accessibility considerations in its iframe reference.
Keep the frame inside a constrained layout
.document-column {
width: min(100%, 72rem);
margin-inline: auto;
padding-inline: 1rem;
}
.pdf-frame {
width: 100%;
min-height: 70vh;
overflow: hidden;
}
The iframe cannot be wider than its parent. Check parent widths, padding, grid tracks, and flex constraints if it appears narrow.
Why object-fit does not work
/* This does not scale the PDF rendered inside the iframe. */
iframe {
object-fit: contain;
}
MDN explicitly states that object-fit has no effect on <iframe> elements. It can be used for replaced content such as images, but it does not command the native PDF viewer to change its page zoom.
2. Make the PDF page itself fit the width
Outer CSS controls the iframe rectangle, not the page scale selected by the browser’s PDF viewer. If every page must use a predictable fit-to-width scale, render the document through a viewer you control, such as PDF.js.
PDF.js scaling calculation
PDF.js exposes a page viewport. Measure the page at scale 1, divide the available width by that width, then render with the resulting scale. The official PDF.js examples show this viewport approach and account for device-pixel ratio when sizing the canvas backing store.
<canvas id="pdf-canvas" aria-label="PDF page 1"></canvas>
<script type="module">
import * as pdfjsLib from "/pdfjs/pdf.mjs";
pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/pdf.worker.mjs";
const canvas = document.querySelector("#pdf-canvas");
const context = canvas.getContext("2d");
const pdf = await pdfjsLib.getDocument("/files/guide.pdf").promise;
const page = await pdf.getPage(1);
const baseViewport = page.getViewport({ scale: 1 });
const availableWidth = canvas.parentElement.clientWidth;
const scale = availableWidth / baseViewport.width;
const viewport = page.getViewport({ scale });
const deviceScale = window.devicePixelRatio || 1;
canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`;
canvas.width = Math.floor(viewport.width * deviceScale);
canvas.height = Math.floor(viewport.height * deviceScale);
await page.render({
canvasContext: context,
viewport,
transform: deviceScale !== 1 ? [deviceScale, 0, 0, deviceScale, 0, 0] : null
}).promise;
</script>
For a multi-page viewer, repeat the calculation and render step for each page, add page navigation, and recalculate when the container width changes. A controlled viewer adds implementation work but gives you consistent zoom, page layout, and interaction behavior.
PDF.js URL zoom options
The PDF.js viewer wiki lists page-width, page-height, and page-fit as accepted zoom fragment values, along with numeric zoom forms. For example:
/pdfjs/web/viewer.html?file=/files/guide.pdf#zoom=page-width
The viewer-options page is dated, so confirm the options supported by the PDF.js version you deploy. Its documented file query option requires the PDF path to be on the same server because of JavaScript restrictions. See the PDF.js viewer options documentation.
3. Choose the right embedding method
| Method | Use it when | Trade-off |
|---|---|---|
<iframe> |
You need a simple preview using the browser’s native PDF viewer. | Page zoom and rendering vary by browser; no child fallback content. |
<object> |
You need fallback HTML when the PDF cannot render. | Still depends on browser PDF support and does not provide a controlled page-scale API. |
| PDF.js or another controlled viewer | You need predictable fit-to-width scaling, custom controls, or page-level interaction. | More JavaScript, assets, loading work, and maintenance. |
MDN’s embedding guide covers the fallback difference between iframe and object.
Object fallback example
<object data="/files/guide.pdf" type="application/pdf" width="100%" height="700">
<p>This browser cannot display the PDF inline.
<a href="/files/guide.pdf">Download the PDF</a>.</p>
</object>
4. Accessibility, security, and cross-origin limits
- Give the iframe a descriptive
title, such as “Product guide PDF”. - Keep a normal PDF link outside the frame so users can open or download it independently.
- Do not assume the parent can inspect a cross-origin PDF’s dimensions or internal elements. Browser security rules restrict script access.
- Do not add
sandboxreflexively. MDN warns that sandboxing can prevent a native PDF viewer from loading and is not a portable restriction mechanism for native PDF previews. Test any sandbox policy in every target browser.
For an HTML document that you control, MDN documents an opt-in responsive embedded sizing feature using <meta name="responsive-embedded-sizing">, the frame-sizing CSS property, and Window.requestResize(). This is cooperative document sizing for HTML content, not a general way to read the dimensions of a browser’s native PDF viewer; check browser support before relying on it. See the MDN responsive embedded sizing reference.
5. Responsive patterns that work in real layouts
Aspect-ratio preview
.pdf-frame {
width: 100%;
aspect-ratio: 8.5 / 11;
min-height: 32rem;
}
.pdf-frame iframe {
width: 100%;
height: 100%;
border: 0;
}
An aspect ratio gives a predictable preview shape, but a long PDF still scrolls inside the viewer. Use a viewport height when readers need more vertical space.
Mobile-friendly height
.pdf-frame iframe {
width: 100%;
height: 70vh;
min-height: 28rem;
border: 0;
}
@media (max-width: 40rem) {
.pdf-frame iframe {
height: 80vh;
min-height: 24rem;
}
}
Use a separate breakpoint when browser chrome and narrow screens make a desktop height uncomfortable. Keep the direct link visible if the embedded viewer is difficult to use on a particular device.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The iframe is blank or has no height. | Only width was set, or a parent has no usable height. | Set an explicit or viewport-based iframe height and check parent layout rules. |
| The PDF is clipped. | The frame is shorter than the native viewer’s usable area or an ancestor clips overflow. | Increase the height, remove unintended clipping, or provide a direct link. |
object-fit: contain changes nothing. |
It does not apply to iframe contents. | Use a controlled viewer such as PDF.js for page scaling. |
| The page is not fit to width. | The native viewer chooses its own zoom. | Use PDF.js and calculate the viewport scale from available width, or use a supported PDF.js zoom fragment. |
| The parent cannot measure the PDF. | The frame is cross-origin or the content is a native PDF viewer. | Do not inspect internals; set the outer height deliberately or use a viewer you control. |
The PDF fails after adding sandbox. |
Sandbox restrictions can block native PDF viewer loading. | Remove sandbox or evaluate the required permissions in the target browsers. |
| Users cannot open the document. | No independent fallback was supplied. | Add a normal link next to or below the embed. |
| PDF.js cannot load a remote file. | The viewer’s file option is subject to same-server JavaScript restrictions. | Serve the PDF from the same server as the viewer or configure a supported server-side delivery path. |
7. Performance and reliability checklist
- Add
loading="lazy"when the frame is below the initial viewport. - Keep the native iframe path when you only need a preview; PDF.js adds script, worker, canvas, and per-page rendering work.
- Render only the visible PDF.js page range first, then add navigation and additional pages as needed.
- Resize a controlled viewer when its container changes, rather than rendering at a desktop width on mobile.
- Test the actual browser and device set. Native PDF support and viewer controls are browser-dependent.
- Always retain the direct PDF URL so a viewer failure does not block access to the file.
8. Or skip the browser setup
If your goal is to generate a PDF or image preview of a URL rather than embed an existing PDF, ScreenshotNeo provides a website screenshot API and MCP server. Its PDF endpoint can capture a page without you managing browser automation. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
9. FAQ
Can an iframe automatically grow to the full PDF length?
No. The iframe is a box with a height you choose, while the native viewer manages its internal pages. Use a controlled viewer or a direct PDF link when a single expanding document surface is required.
Should I use iframe or object?
Choose iframe for a straightforward native preview. Choose object when fallback HTML inside the element is important.
Can JavaScript read the PDF’s rendered height?
Not in the general case. Cross-origin rules and the native PDF viewer isolate its internals from the parent page.
What is the simplest reliable CSS?
Use width: 100%, an explicit height such as 70vh, a descriptive title, and a normal link to the PDF.
When is PDF.js worth the extra code?
Use it when fit-to-width behavior, page controls, or consistent rendering matters more than the simplicity of the native viewer.


