<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Boris Zabolotskikh</title>
    <description>The latest articles on DEV Community by Boris Zabolotskikh (@borzab).</description>
    <link>https://dev.to/borzab</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3783914%2Ff82d7911-2002-4bf5-a20a-276a87c332bc.jpg</url>
      <title>DEV Community: Boris Zabolotskikh</title>
      <link>https://dev.to/borzab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/borzab"/>
    <language>en</language>
    <item>
      <title>How to Build a Web Page to PDF Converter and Not Lose Your Mind</title>
      <dc:creator>Boris Zabolotskikh</dc:creator>
      <pubDate>Sat, 21 Feb 2026 12:58:20 +0000</pubDate>
      <link>https://dev.to/borzab/how-to-build-a-web-page-to-pdf-converter-and-not-lose-your-mind-2kp0</link>
      <guid>https://dev.to/borzab/how-to-build-a-web-page-to-pdf-converter-and-not-lose-your-mind-2kp0</guid>
      <description>&lt;p&gt;Have you ever wanted to save an article as a PDF without all the extra junk — just clean text?&lt;br&gt;&lt;br&gt;
Or save only a specific part of a page?&lt;br&gt;&lt;br&gt;
And have everything on one long page, without page breaks?&lt;/p&gt;

&lt;p&gt;Yeah. Same here.&lt;br&gt;&lt;br&gt;
So I decided to build my own solution and turn it into a browser extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s wrong with the standard CTRL+P?
&lt;/h2&gt;

&lt;p&gt;When I save something as a PDF, I want it to look &lt;em&gt;exactly&lt;/em&gt; the way it looks on the screen.&lt;br&gt;&lt;br&gt;
I want clickable links.&lt;br&gt;&lt;br&gt;
I want the whole document on one long page, without breaks.&lt;/p&gt;

&lt;p&gt;CTRL+P can’t do that. It’s good only for printing on paper.&lt;br&gt;&lt;br&gt;
Which is exactly what it was originally designed for.&lt;br&gt;&lt;br&gt;
Everything else it simply doesn’t know how to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Available modes in the extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Full page
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjtqaf2lexrhczb8ivvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyjtqaf2lexrhczb8ivvn.png" alt="Full page mode" width="530" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Saves the entire page exactly as you see it on the screen.&lt;br&gt;&lt;br&gt;
The text stays selectable, and links remain clickable.&lt;/p&gt;

&lt;p&gt;This is not an image with OCR text.&lt;br&gt;&lt;br&gt;
It’s a real PDF with real text and real links.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export a page element
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4735rv8iyctfka24dh1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4735rv8iyctfka24dh1.gif" alt="Export a page element mode" width="760" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this mode, you can select a specific element on the page and export only that.&lt;br&gt;&lt;br&gt;
This is very handy when you want to save just an article, just a code block, or any other element.&lt;/p&gt;

&lt;p&gt;I often use this mode when submitting Google Forms and want to save the filled-in data, so I don’t forget what I sent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Article
&lt;/h3&gt;

&lt;p&gt;One of the most interesting modes.&lt;br&gt;&lt;br&gt;
It lets you save blog articles in a reader-friendly view. Nothing extra on the page — just the article itself.&lt;/p&gt;

&lt;p&gt;In this mode, I made sure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code blocks wrap lines properly
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tags are expanded
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the entire article content ends up in the PDF.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove elements
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lwk10v65b64zkgt0tpn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lwk10v65b64zkgt0tpn.gif" alt="Remove elements mode" width="760" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this mode is enabled, you can click on any element on the page and remove it.&lt;br&gt;&lt;br&gt;
For example: sidebars, menus, ads, and so on.&lt;/p&gt;

&lt;p&gt;If you accidentally remove something important, just press &lt;strong&gt;CTRL+Z&lt;/strong&gt; to undo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export chats from ChatGPT, DeepSeek, and Gemini
&lt;/h3&gt;

&lt;p&gt;Since AI companies don’t really want to add PDF export for chats, I did it in my extension. Why not?&lt;/p&gt;

&lt;p&gt;It’s very convenient — one click and you save the currently open conversation.&lt;/p&gt;

&lt;p&gt;In every mode, you can choose the layout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single-page PDF
&lt;/li&gt;
&lt;li&gt;multi-page PDF
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on whether you want to print it or just read it without page breaks.&lt;/p&gt;

&lt;p&gt;You can also adjust the layout to match the screen size or standard formats like &lt;strong&gt;A4&lt;/strong&gt;, &lt;strong&gt;A5&lt;/strong&gt;, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  How not to lose your mind
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Too many layout variations
&lt;/h3&gt;

&lt;p&gt;Saving websites to PDF is hard.&lt;br&gt;&lt;br&gt;
It’s impossible to account for every layout variation. Something will always break somewhere.&lt;/p&gt;

&lt;p&gt;At first, I tried fixing issues site by site, but quickly realized this was a fight against windmills.&lt;br&gt;&lt;br&gt;
So now I only add special fixes for large platforms like Notion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic CSS
&lt;/h3&gt;

&lt;p&gt;Because of the massive adoption of atomic CSS frameworks like Tailwind, it’s hard to reliably select elements for export on some sites.&lt;/p&gt;

&lt;p&gt;For example, exporting a ChatGPT conversation required quite a bit of work and some tricky selectors just to grab the dialog element.&lt;/p&gt;

&lt;p&gt;With Claude Code, I gave up entirely because of the layout.&lt;br&gt;&lt;br&gt;
Gemini was the easiest — the class names were actually human-readable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy-loading
&lt;/h3&gt;

&lt;p&gt;Lazy-loaded images are a whole separate kind of pain.&lt;/p&gt;

&lt;p&gt;Imagine a long page with tons of images. To make sure all images load and end up in the PDF, the best solution I found was this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loop through all images
&lt;/li&gt;
&lt;li&gt;wait ~100 ms near each one
&lt;/li&gt;
&lt;li&gt;move on to the next
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to trigger image loading.&lt;/p&gt;

&lt;p&gt;If you have a more elegant solution — I’d love to hear it in the comments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restoring page styles
&lt;/h3&gt;

&lt;p&gt;The “export page element” mode caused problems too.&lt;/p&gt;

&lt;p&gt;To export a single element, I hide everything else on the page except that element.&lt;br&gt;&lt;br&gt;
Then I restore all styles back — without reloading the page.&lt;/p&gt;

&lt;p&gt;Reloading is not an option.&lt;br&gt;&lt;br&gt;
If you filled out a long form and want to save it as a PDF, and the page reloads in the process — the rage will be real.&lt;br&gt;&lt;br&gt;
I know, I’ve been there.&lt;/p&gt;

&lt;h3&gt;
  
  
  No documentation
&lt;/h3&gt;

&lt;p&gt;Rendering the final PDF is a whole story on its own.&lt;br&gt;&lt;br&gt;
There are no good libraries for this task.&lt;/p&gt;

&lt;p&gt;There’s the well-known &lt;strong&gt;PDF.js&lt;/strong&gt; by Mozilla, which includes &lt;strong&gt;PDFViewer&lt;/strong&gt;. Sounds great — except for one thing: there’s basically no documentation.&lt;/p&gt;

&lt;p&gt;You have to figure everything out by reading source code and GitHub issues.&lt;/p&gt;

&lt;p&gt;There are tons of issues and discussions asking for documentation, but Mozilla doesn’t want to do it.&lt;br&gt;&lt;br&gt;
Fair enough. At least thanks for the library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the hood
&lt;/h2&gt;

&lt;p&gt;Here are the main tools I use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://wxt.dev/" rel="noopener noreferrer"&gt;WXT.dev&lt;/a&gt; — a framework for building browser extensions. In my opinion, the best option right now: fast HMR, separate browser for testing, great dev speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mozilla.github.io/pdf.js/getting_started/" rel="noopener noreferrer"&gt;PDFViewer&lt;/a&gt; — for rendering PDFs. It’s painful, but there are no real alternatives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://chromedevtools.github.io/devtools-protocol/tot/Page/" rel="noopener noreferrer"&gt;Chrome Debugger&lt;/a&gt; — for converting pages to PDF. Sounds weird at first, but it gives the highest final PDF quality.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;I keep adding integrations with large websites.&lt;br&gt;&lt;br&gt;
For example, I recently added one-click saving for Reddit posts.&lt;/p&gt;

&lt;p&gt;Besides the &lt;a href="https://chromewebstore.google.com/detail/web-to-pdf/pamnlaoeobcmhkliljfaofekeddpmfoh?utm_source=devto" rel="noopener noreferrer"&gt;extension&lt;/a&gt;, I also built a &lt;a href="https://webtopdf.space/?utm_source=devto#demo" rel="noopener noreferrer"&gt;web service&lt;/a&gt; for saving pages as PDFs. It doesn’t have as many features as the extension, but it works on mobile devices.&lt;/p&gt;

</description>
      <category>extensions</category>
      <category>pdf</category>
      <category>pdfviewer</category>
    </item>
  </channel>
</rss>
