<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ webdevelopment - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ webdevelopment - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 26 Jul 2026 14:12:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/webdevelopment/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Browser-Based PDF Resizer Using JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ PDF documents come in many different page sizes. Some are designed for A4 paper, while others use Letter, Legal, Tabloid, or custom dimensions. This can create problems when printing, sharing, archivi ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-pdf-resizer-javascript/</link>
                <guid isPermaLink="false">6a4308a0b89cf8453d414d2e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdevelopment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pdf ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Online PDF Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Tue, 30 Jun 2026 00:06:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/1b1d6bca-dec7-4b4c-8983-0b2a94ba8f8e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>PDF documents come in many different page sizes. Some are designed for A4 paper, while others use Letter, Legal, Tabloid, or custom dimensions. This can create problems when printing, sharing, archiving, or submitting documents to organizations that require a specific page format.</p>
<p>A PDF resizing tool solves this problem by allowing users to change the page dimensions of an existing PDF without recreating the document from scratch.</p>
<p>Whether you're preparing business reports, invoices, scanned documents, eBooks, presentations, or government forms, resizing pages helps ensure documents fit the required paper size while maintaining a professional appearance.</p>
<p>In this tutorial, you'll build a browser-based PDF Resizer using JavaScript. Users will be able to upload PDF files, preview document pages, choose preset or custom page sizes, configure scaling behavior, add page margins, and generate a resized PDF directly inside the browser.</p>
<p>Everything happens locally using JavaScript, so uploaded documents never leave the user's device. This improves privacy, eliminates server-side processing, and provides a faster experience.</p>
<p>By the end of this guide, you'll understand how browser-based PDF resizing works and how to build a practical tool that can be extended with additional document editing features.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/b895666f-44f2-461f-8f10-7ad1443b35a1.png" alt="allinonetools pdf tools kit pdf resize tool" style="display:block;margin:0 auto" width="619" height="308" loading="lazy">

<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-why-pdf-resizing-is-useful">Why PDF Resizing Is Useful</a></p>
</li>
<li><p><a href="#heading-how-pdf-resizing-works">How PDF Resizing Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-what-library-are-we-using">What Library Are We Using?</a></p>
</li>
<li><p><a href="#heading-creating-the-upload-interface">Creating the Upload Interface</a></p>
</li>
<li><p><a href="#heading-previewing-uploaded-pdf-pages">Previewing Uploaded PDF Pages</a></p>
</li>
<li><p><a href="#heading-configuring-resize-settings">Configuring Resize Settings</a></p>
</li>
<li><p><a href="#heading-applying-the-resize">Applying the Resize</a></p>
</li>
<li><p><a href="#heading-generating-the-resized-pdf">Generating the Resized PDF</a></p>
</li>
<li><p><a href="#heading-demo-how-the-pdf-resize-tool-works">Demo: How the PDF Resize Tool Works</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-pdf-resizing-is-useful">Why PDF Resizing Is Useful</h2>
<p>PDF files are created for many different purposes, and not every document uses the same page dimensions. A presentation might be designed for widescreen viewing, an invoice could use Letter size, a government application may require A4 paper, and engineering drawings often use Legal or Tabloid formats.</p>
<p>When documents don't match the required page size, they can be difficult to print correctly. Content may appear cut off, excessive white space may be introduced, or the document may scale incorrectly during printing.</p>
<p>A PDF resizing tool helps solve these problems by allowing users to convert pages into standardized sizes without rebuilding the document.</p>
<p>This is especially useful in everyday situations. Businesses often receive invoices and contracts from different organizations that use different paper standards. Before storing or printing these documents, employees frequently resize them to a consistent format.</p>
<p>Students regularly download lecture notes, assignments, research papers, and ebooks from different sources. Converting these files to a common page size makes printing and reading much easier.</p>
<p>Graphic designers and publishers also resize PDFs before sending artwork for commercial printing, ensuring that documents match the required dimensions of the printing service.</p>
<p>One practical example comes from e-commerce businesses. Imagine a seller who receives hundreds of shipping labels every day from platforms like Amazon, Flipkart, or Meesho. Different marketplaces may generate labels using different paper sizes. Before printing them in bulk, the seller can resize every PDF to A4 so all labels print consistently without manual adjustments.</p>
<p>Government offices, banks, educational institutions, and legal firms also work with PDFs from multiple sources. Standardizing page sizes simplifies document management, digital archiving, scanning, and future editing.</p>
<p>Instead of recreating an entire document, resizing allows users to adapt existing PDFs quickly while preserving their original content.</p>
<h2 id="heading-how-pdf-resizing-works">How PDF Resizing Works</h2>
<p>A PDF resizing tool changes the dimensions of one or more pages inside an existing PDF document while preserving the page content.</p>
<p>When a user uploads a PDF, the browser first reads the document and extracts information such as page count, page dimensions, and page objects. Instead of editing the original file directly, a new PDF is created with the selected page size, and each page is copied into the new document using the chosen scaling method.</p>
<p>Depending on the resize settings, the page content can be scaled to fill the page, stretched to match the new dimensions, centered without scaling, or cropped to fit the selected paper size.</p>
<p>Users can also choose whether the resize operation should apply to every page or only a specific page range. This is useful when only part of a document needs to be converted to another paper size.</p>
<p>Because all processing happens inside the browser, uploaded files remain on the user's device throughout the entire workflow. No documents are sent to external servers, making the tool suitable for confidential reports, contracts, invoices, educational documents, business records, and other sensitive files.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Create a new project folder for the PDF resizer.</p>
<p>A simple project structure looks like this:</p>
<pre><code class="language-text">pdf-resizer/
│── index.html
│── style.css
│── script.js
│── assets/
</code></pre>
<p>The HTML file contains the upload area, page preview, resize settings, and download section.</p>
<p>The CSS file handles the overall layout and responsive design.</p>
<p>The JavaScript file manages PDF loading, page previews, resize operations, and exporting the updated document.</p>
<p>Keeping the project organized makes it easier to add new features later, such as cropping, page rotation, watermarking, metadata editing, or PDF merging.</p>
<h2 id="heading-what-library-are-we-using">What Library Are We Using?</h2>
<p>This project uses <strong>pdf-lib</strong>, one of the most popular JavaScript libraries for creating and editing PDF documents directly in the browser.</p>
<p>It allows developers to:</p>
<ul>
<li><p>Read existing PDF files.</p>
</li>
<li><p>Create new PDF documents.</p>
</li>
<li><p>Copy pages between PDFs.</p>
</li>
<li><p>Resize pages.</p>
</li>
<li><p>Rotate pages.</p>
</li>
<li><p>Add text, images, and shapes.</p>
</li>
<li><p>Modify document metadata.</p>
</li>
<li><p>Export updated PDFs.</p>
</li>
</ul>
<p>Unlike many online PDF services, <strong>pdf-lib</strong> works entirely in the browser and doesn't require a backend server.</p>
<p>Install the library using npm:</p>
<pre><code class="language-bash">npm install pdf-lib
</code></pre>
<p>Or include it directly from a CDN:</p>
<pre><code class="language-html">&lt;script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js"&gt;&lt;/script&gt;
</code></pre>
<p>Once loaded, the library exposes the <code>PDFDocument</code> object that is used throughout the application.</p>
<p>Example:</p>
<pre><code class="language-javascript">const { PDFDocument } = PDFLib;
</code></pre>
<h2 id="heading-creating-the-upload-interface">Creating the Upload Interface</h2>
<p>The first step is allowing users to upload a PDF document.</p>
<p>The interface includes a drag-and-drop area together with a traditional file picker so users can choose whichever method they prefer.</p>
<p>When a file is selected, JavaScript validates that it is a PDF before loading it into memory.</p>
<p>Here's a simple upload input:</p>
<pre><code class="language-html">&lt;input
    type="file"
    id="pdfUpload"
    accept="application/pdf"
/&gt;
</code></pre>
<p>Next, listen for file selection:</p>
<pre><code class="language-javascript">const upload = document.getElementById("pdfUpload");

upload.addEventListener("change", async (event) =&gt; {
    const file = event.target.files[0];

    if (!file) return;

    console.log(file.name);
});
</code></pre>
<p>Read the uploaded file:</p>
<pre><code class="language-javascript">const bytes = await file.arrayBuffer();

const pdfDoc = await PDFLib.PDFDocument.load(bytes);
</code></pre>
<p>At this point, the uploaded PDF is loaded into memory and is ready for preview generation and resizing.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/a8630362-557c-4cd5-9f9a-3805134884e5.png" alt="PDF upload interface with drag-and-drop area for selecting a PDF file to resize." style="display:block;margin:0 auto" width="624" height="629" loading="lazy">

<h2 id="heading-previewing-uploaded-pdf-pages">Previewing Uploaded PDF Pages</h2>
<p>After the document has been uploaded, the next step is generating page previews.</p>
<p>Displaying page thumbnails helps users confirm that the correct file has been selected before changing its size.</p>
<p>For multi-page PDFs, previewing every page also makes it easier to understand how the resize operation will affect the document.</p>
<p>Your tool displays thumbnails for every page, allowing users to visually inspect the PDF before making any modifications.</p>
<p>Developers can retrieve the total number of pages using <strong>pdf-lib</strong>:</p>
<pre><code class="language-javascript">const totalPages = pdfDoc.getPageCount();

console.log(totalPages);
</code></pre>
<p>Retrieve individual pages:</p>
<pre><code class="language-javascript">const pages = pdfDoc.getPages();

pages.forEach((page, index) =&gt; {
    console.log(`Page ${index + 1}`);
});
</code></pre>
<p>Once the pages are available, the application can generate preview thumbnails and display them inside the browser.</p>
<p>Users can then decide whether to resize the entire document or only selected pages.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/76fbd44e-c359-4239-9add-56c44356adf9.png" alt="Uploaded PDF page thumbnails displayed before resizing the document" style="display:block;margin:0 auto" width="594" height="641" loading="lazy">

<h2 id="heading-configuring-resize-settings">Configuring Resize Settings</h2>
<p>Once the PDF pages have been loaded, users need to configure how the document should be resized.</p>
<p>A flexible PDF resizing tool should support both standard paper sizes and custom dimensions while allowing users to control how the existing content fits inside the new page.</p>
<p>In this project, users can choose whether the resize operation should be applied to every page or only a specific page range.</p>
<p>For page dimensions, the tool supports popular paper formats such as A4, A5, Letter, Legal, Tabloid, and Square. If none of these meet the user's requirements, custom width and height values can also be entered.</p>
<p>Another important setting is content scaling. Depending on the document, users may want to fit the existing content inside the page, stretch it to fill the available space, keep the original size centered on the page, or crop the content to match the selected paper size.</p>
<p>The resize panel also includes margin controls so users can add additional spacing around the document if required.</p>
<p>Together, these settings provide complete control over the final page layout before generating the resized PDF.</p>
<h3 id="heading-choosing-preset-paper-sizes">Choosing Preset Paper Sizes</h3>
<p>Many users simply want to convert documents into common paper formats.</p>
<p>Instead of entering page dimensions manually, the tool provides several predefined page sizes that can be selected with a single click.</p>
<p>These presets are especially useful when preparing PDFs for printing, business reports, contracts, government forms, books, or educational documents.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/c1548fd3-d401-4714-87cc-eceffc9a3c6f.png" alt=" Preset paper size options including A4, A5, Letter, Legal, Tabloid, and Square." style="display:block;margin:0 auto" width="604" height="207" loading="lazy">

<h3 id="heading-using-custom-page-sizes">Using Custom Page Sizes</h3>
<p>If a standard paper size isn't suitable, users can enter custom page dimensions.</p>
<p>The width and height can be specified using supported measurement units, allowing documents to match exact printing or publishing requirements.</p>
<p>The <strong>Lock Aspect Ratio</strong> option ensures that the document maintains its original proportions while resizing.</p>
<h3 id="heading-selecting-content-scaling">Selecting Content Scaling</h3>
<p>Different documents require different scaling behavior.</p>
<p>The tool offers several scaling modes so users can choose the most appropriate layout for their document.</p>
<p>For example, <strong>Fit to Page</strong> scales content while preserving proportions, <strong>Stretch to Fit</strong> expands the content to fill the page completely, <strong>Keep Original Size (Center)</strong> places the original page in the center without scaling, and <strong>Crop to Fit</strong> trims overflowing content to match the selected page dimensions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/c66fde6a-6a3e-4e36-af4a-b9c508358f34.png" alt="Content scaling options including Fit to Page, Stretch to Fit, Keep Original Size, and Crop to Fit." style="display:block;margin:0 auto" width="603" height="201" loading="lazy">

<h3 id="heading-example-resize-settings">Example Resize Settings</h3>
<p>Here's a simplified configuration object that stores the selected resize options.</p>
<pre><code class="language-javascript">const resizeOptions = {
    width: 210,
    height: 297,
    unit: "mm",
    scaleMode: "fit",
    applyTo: "all"
};
</code></pre>
<p>The selected values are then used while generating the resized PDF.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/bc1f16f4-4f38-4fd5-b553-51da4fc03ee3.png" alt="Resize settings panel showing page selection, custom dimensions, content scaling, and margin controls." style="display:block;margin:0 auto" width="628" height="687" loading="lazy">

<h2 id="heading-applying-the-resize">Applying the Resize</h2>
<p>After the resize settings have been configured, the application creates a new PDF document and copies each page into it using the selected dimensions.</p>
<p>Each page is resized according to the chosen paper size and scaling mode before being added to the new document.</p>
<p>A simplified example looks like this:</p>
<pre><code class="language-javascript">const newPdf = await PDFLib.PDFDocument.create();

const copiedPages = await newPdf.copyPages(
    pdfDoc,
    pdfDoc.getPageIndices()
);

copiedPages.forEach(page =&gt; {
    page.setSize(595, 842);

    newPdf.addPage(page);
});
</code></pre>
<p>The dimensions shown above represent an A4 page measured in PDF points.</p>
<p>Developers can replace these values with custom dimensions depending on the selected paper size.</p>
<p>After every page has been resized, the new document is ready for export.</p>
<h2 id="heading-generating-the-resized-pdf">Generating the Resized PDF</h2>
<p>Once all pages have been processed, the updated document is converted into a downloadable PDF.</p>
<p>The browser creates a binary PDF file and generates a temporary download link without sending the document to a server.</p>
<p>Saving the resized document is straightforward.</p>
<pre><code class="language-javascript">const pdfBytes = await newPdf.save();

const blob = new Blob([pdfBytes], {
    type: "application/pdf"
});

const url = URL.createObjectURL(blob);
</code></pre>
<p>The generated URL can then be attached to a download button.</p>
<pre><code class="language-javascript">const link = document.createElement("a");

link.href = url;
link.download = "resized-document.pdf";

link.click();
</code></pre>
<p>The user immediately receives the updated document with the newly selected page dimensions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/6f390adc-e404-4381-a6de-b0b5b424f2fe.png" alt="Resize PDF button used to generate the resized document." style="display:block;margin:0 auto" width="295" height="81" loading="lazy">

<h2 id="heading-demo-how-the-pdf-resize-tool-works">Demo: How the PDF Resize Tool Works</h2>
<h3 id="heading-step-1-upload-your-pdf-file">Step 1: Upload Your PDF File</h3>
<p>The process begins by uploading a PDF document into the browser.</p>
<p>Users can either drag and drop a file into the upload area or choose a document using the file picker.</p>
<p>Once selected, the browser validates the file type and loads the document locally without uploading it to a server. This keeps the entire resizing process private and ensures sensitive documents remain on the user's device.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/6711451f-2b38-4215-b0f2-f2f9d53c6914.png" alt=" PDF upload interface with drag-and-drop area for resizing PDF pages" style="display:block;margin:0 auto" width="624" height="629" loading="lazy">

<h3 id="heading-step-2-preview-uploaded-pdf-pages">Step 2: Preview Uploaded PDF Pages</h3>
<p>After the PDF has been loaded, the tool generates page previews for the entire document.</p>
<p>Displaying thumbnails allows users to verify that the correct document has been selected before making any modifications.</p>
<p>Users can also select individual pages if they only want to resize certain parts of the document.</p>
<p>This preview step helps avoid mistakes before generating the final PDF.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/b50a41d0-cea3-44b9-bcc0-934a64b367ee.png" alt="Uploaded PDF page thumbnails displayed before resizing." style="display:block;margin:0 auto" width="622" height="727" loading="lazy">

<h3 id="heading-step-3-configure-resize-settings">Step 3: Configure Resize Settings</h3>
<p>Next, users configure how the document should be resized.</p>
<p>The tool supports standard paper sizes such as A4, A5, Letter, Legal, Tabloid, and Square, while also allowing completely custom dimensions.</p>
<p>Users can specify whether the resize operation should apply to all pages or only selected page ranges.</p>
<p>Additional options include locking the aspect ratio, choosing page orientation, selecting a content scaling mode, and adding page margins when necessary.</p>
<p>These settings provide complete control over the final document layout before processing begins.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/d01f0c31-135e-4997-8b22-23e866d17e63.png" alt="PDF resize settings showing paper size selection, page range, scaling options, and custom dimensions. " style="display:block;margin:0 auto" width="628" height="687" loading="lazy">

<h3 id="heading-step-4-generate-the-resized-pdf">Step 4: Generate the Resized PDF</h3>
<p>After reviewing the selected options, users simply click the <strong>Resize PDF</strong> button.</p>
<p>The browser processes every selected page according to the configured paper size and scaling method.</p>
<p>Because the entire operation runs locally, even large documents can usually be processed within a few seconds depending on the number of pages.</p>
<p>If users want to process another document, the <strong>Start Over</strong> button clears the current session and returns the tool to its initial state.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/a3da0b1d-6655-433c-802f-81af05787a1f.png" alt="Resize PDF button with Start Over option." style="display:block;margin:0 auto" width="295" height="81" loading="lazy">

<h3 id="heading-step-5-preview-the-resized-pdf">Step 5: Preview the Resized PDF</h3>
<p>Once processing is complete, the newly generated PDF is displayed inside the browser.</p>
<p>Users can review the resized pages before downloading the document.</p>
<p>The preview section includes page navigation controls, making it easy to move through multi-page PDFs and confirm that every page has been resized correctly.</p>
<p>Reviewing the output before download helps catch formatting issues and ensures the selected page size and scaling options produced the expected results.</p>
<h3 id="heading-step-6-download-the-final-pdf">Step 6: Download the Final PDF</h3>
<p>After confirming the resized document, users can download the updated PDF.</p>
<p>The final output section displays useful information such as the output filename, total number of pages, and file size.</p>
<p>Users may also rename the document before downloading it, making it easier to organize files after processing.</p>
<p>The <strong>Download PDF</strong> button saves the resized document, while the <strong>Start Over</strong> button allows users to upload another PDF without refreshing the page.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/12d5074e-d534-4b91-b3af-79d846e35041.png" alt="Alt text: Resized PDF ready for download showing filename, page count, file size, and download button." style="display:block;margin:0 auto" width="624" height="397" loading="lazy">

<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>Resizing PDF pages can significantly improve document compatibility, but it's important to validate uploaded files before processing.</p>
<p>For example:</p>
<pre><code class="language-javascript">if (file.type !== "application/pdf") {
    alert("Please upload a valid PDF file.");
    return;
}
</code></pre>
<p>Very large PDF files may require additional processing time depending on the number of pages and embedded images.</p>
<p>When resizing documents containing hundreds of pages, processing them page by page can help reduce memory usage.</p>
<p>Before generating the final PDF, it's also a good idea to verify the selected page size.</p>
<p>For example:</p>
<pre><code class="language-javascript">console.log(`Selected Size: ${pageSize}`);
console.log(`Scale Mode: ${scaleMode}`);
</code></pre>
<p>Previewing the resized document before downloading helps ensure that text, images, and page layouts appear correctly.</p>
<p>Because everything happens inside the browser, uploaded documents remain on the user's device throughout the entire resizing process.</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<p>One common mistake is selecting a page size that doesn't match the intended output.</p>
<p>For example, resizing a wide presentation directly to A4 portrait may cause content to become too small or appear compressed.</p>
<p>Always verify the selected paper size before processing.</p>
<pre><code class="language-javascript">if (pageWidth &lt;= 0 || pageHeight &lt;= 0) {
    alert("Invalid page dimensions.");
}
</code></pre>
<p>Another common mistake is choosing the wrong content scaling mode.</p>
<p>Stretching content may distort images and text, while cropping may remove important information from the page.</p>
<p>Users should preview different scaling modes before generating the final PDF.</p>
<p>It's also important to check whether the resize operation should apply to every page or only selected pages.</p>
<pre><code class="language-javascript">const applyMode = "all";

console.log(`Resize Mode: ${applyMode}`);
</code></pre>
<p>Finally, always review the generated PDF before downloading it.</p>
<p>Taking a few seconds to inspect page layouts, margins, and scaling can prevent unnecessary reprocessing later.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based PDF Resizer using JavaScript.</p>
<p>You learned how to upload PDF files, preview document pages, configure page sizes, adjust scaling behavior, resize pages, and generate downloadable PDF files directly inside the browser.</p>
<p>More importantly, you saw how modern browsers can perform PDF page resizing locally without requiring a backend server.</p>
<p>This approach keeps document processing fast, private, and easy to use while giving users complete control over the final document layout.</p>
<p>If you'd like to see a working example, try the <a href="https://allinonetools.net/resize-pdf/"><strong>PDF Resize Tool</strong></a> and explore how PDF pages can be resized directly in your browser.</p>
<p>Once you understand this workflow, you can extend it further with features like page cropping, rotation, watermarking, metadata editing, page numbering, document organization, and other advanced PDF editing capabilities.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Stop Letting AI Agents Guess Your Requirements ]]>
                </title>
                <description>
                    <![CDATA[ I spent 64% of my weekly Claude budget before Wednesday building a tool designed to reduce Claude usage. That's the kind of irony that deserves its own specification. The tool is spec-writer: a Claude ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-stop-letting-ai-agents-guess-your-requirements/</link>
                <guid isPermaLink="false">69c1dc5930a9b81e3ac400fc</guid>
                
                    <category>
                        <![CDATA[ claude.ai ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ClaudeCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ claude ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdevelopment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Nwaneri ]]>
                </dc:creator>
                <pubDate>Tue, 24 Mar 2026 00:35:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/06a3ff85-4d60-4e05-b494-8d2f3e6024ac.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I spent 64% of my weekly Claude budget before Wednesday building a tool designed to reduce Claude usage. That's the kind of irony that deserves its own specification.</p>
<p>The tool is spec-writer: a Claude Code skill that takes a vague feature request and generates a structured spec, technical plan, and task breakdown before a single line of code gets written.</p>
<p>The problem it solves is one most developers hit within their first week of using AI coding agents seriously: the agent writes confidently in the wrong direction and you pay for it twice, once in tokens, once in rewrites.</p>
<p>This tutorial shows you how to install spec-writer, how to invoke it on a real feature, and how to read the output so you can catch the assumptions that would have wasted your time.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-the-problem-with-prompting-agents-directly">The Problem with Prompting Agents Directly</a></p>
</li>
<li><p><a href="#heading-what-specdriven-development-is">What Spec-Driven Development Is</a></p>
</li>
<li><p><a href="#heading-how-spec-writer-works">How spec-writer Works</a></p>
</li>
<li><p><a href="#heading-how-to-install-spec-writer">How to Install spec-writer</a></p>
</li>
<li><p><a href="#heading-how-to-write-your-first-spec">How to Write Your First Spec</a></p>
</li>
<li><p><a href="#heading-how-to-read-the-output">How to Read the Output</a></p>
</li>
<li><p><a href="#heading-how-to-hand-the-spec-to-your-agent">How to Hand the Spec to Your Agent</a></p>
</li>
<li><p><a href="#heading-where-to-go-next">Where to Go Next</a></p>
</li>
</ol>
<h2 id="heading-the-problem-with-prompting-agents-directly">The Problem with Prompting Agents Directly</h2>
<p>Here is what happens when you skip the spec.</p>
<p>You have a feature in your head: "Add a way for users to export their data." You open Claude Code and describe it. The agent produces code. It looks right. You run it. It's mostly right – except it exports everything including soft-deleted records, it doesn't paginate, it times out on large accounts, and it has no authentication check on the export endpoint.</p>
<p>None of those things were in your prompt. The agent guessed, and it guessed plausibly – which is worse than guessing obviously wrong. You didn't notice until testing.</p>
<p>This is the fundamental problem with prompting agents directly on anything non-trivial: your prompt carries your conscious requirements, but every feature has a shadow of requirements you didn't think to state. And the agent fills that shadow with assumptions.</p>
<p>Most of the time, those assumptions are reasonable. Some of the time, they're wrong in ways that take hours to unravel.</p>
<p>The failure mode isn't hallucination. It's the agent being exactly as helpful as the prompt allowed, which wasn't helpful enough.</p>
<p>Spec-Driven Development addresses this directly. The methodology – documented extensively by practitioners like Julián Deangelis – argues that a written spec isn't documentation overhead. It's the mechanism that forces you to make decisions before the agent does.</p>
<h2 id="heading-what-spec-driven-development-is">What Spec-Driven Development Is</h2>
<p>Spec-Driven Development is the practice of writing a structured specification before you write code or prompt an agent. The spec defines what the feature must do, what assumptions are being made, and what tasks the implementation breaks into.</p>
<p>The key insight is what a spec is <em>for</em>. A spec is not trying to replace code. It's trying to surface the decisions that would otherwise be invisible. The agent will make those decisions either way: with a spec, you make them first. Without a spec, you discover them during testing.</p>
<p>The strongest counterargument to SDD comes from Gabriella Gonzalez: <em>a sufficiently detailed spec is just code</em>. She's right that some specs devolve into pseudocode so specific they might as well be implementations.</p>
<p>But that's a spec written at the wrong level of abstraction. The goal is to name the decisions, not to pre-implement them. "Only authenticated users can trigger this export" is a decision. "Call <code>verifyJWT(token)</code> and return 401 if it fails" is implementation. The spec needs the first. The agent handles the second.</p>
<p>SDD has three levels:</p>
<ol>
<li><p><strong>Spec-First</strong>: write a spec before every feature and hand it to the agent as context. This is the entry point and the workflow this tutorial focuses on.</p>
</li>
<li><p><strong>Spec-Anchored</strong>: the spec lives in the repository and evolves alongside the code. When requirements change, you update the spec and re-prompt the agent to realign.</p>
</li>
<li><p><strong>Spec-as-Source</strong>: the spec is the primary artifact. Code is generated from it and considered disposable. This is the most ambitious level and the direction many teams are moving toward.</p>
</li>
</ol>
<p>spec-writer gets you to Spec-First immediately, with no ceremony.</p>
<h2 id="heading-how-spec-writer-works">How spec-writer Works</h2>
<p>spec-writer is a Claude Code skill – a markdown file that loads into the agent's context and changes how it responds when invoked.</p>
<p>The skill follows one rule: generate first, flag assumptions inline. Instead of asking you clarifying questions before producing output, it generates the full spec immediately and marks every decision it made without your explicit input using <code>[ASSUMPTION: ...]</code> tags. Then you correct what's wrong.</p>
<p>This is faster than Q&amp;A because it makes the decisions visible in a form you can react to rather than anticipate.</p>
<p>The output has three sections in fixed order:</p>
<ol>
<li><p><strong>SPEC</strong>: the what. One-line purpose, user stories, requirements, edge cases, and acceptance criteria in Given/When/Then format.</p>
</li>
<li><p><strong>PLAN</strong>: the how. Stack and architecture decisions, data model changes, API contracts, testing strategy, and security constraints.</p>
</li>
<li><p><strong>TASKS</strong>: the breakdown. Ordered, self-contained tasks each completable in a single agent session, each with its own acceptance criteria.</p>
</li>
</ol>
<p>After the three sections, the skill produces an <strong>Assumptions summary</strong>: every <code>[ASSUMPTION: ...]</code> from the output, ranked by impact. This is the part you review before handing anything to the agent.</p>
<p>The skill is compatible with <a href="https://github.com/github/spec-kit">GitHub Spec Kit</a> and <a href="https://github.com/Fission-AI/OpenSpec">OpenSpec</a>. If you use either framework, save the spec output to your <code>.specify/</code> or <code>openspec/changes/</code> directory and continue from there.</p>
<h2 id="heading-how-to-install-spec-writer">How to Install spec-writer</h2>
<p>spec-writer uses the Agent Skills standard, which means the same SKILL.md file works across Claude Code, Cursor, GitHub Copilot, Gemini CLI, and any other agent that supports the standard. You install it once and it works everywhere.</p>
<h3 id="heading-installation">Installation</h3>
<p>Create the skills directory if it doesn't exist and clone the repo:</p>
<pre><code class="language-bash">mkdir -p ~/.claude/skills
git clone https://github.com/dannwaneri/spec-writer.git ~/.claude/skills/spec-writer
</code></pre>
<p>On Windows PowerShell:</p>
<p><strong>(Note:</strong> PowerShell uses backtick (<code>`</code>) for line continuation, not backslash.)</p>
<pre><code class="language-powershell">New-Item -ItemType Directory -Force -Path "$HOME\.claude\skills"
git clone https://github.com/dannwaneri/spec-writer.git "$HOME\.claude\skills\spec-writer"
</code></pre>
<p>That's the entire installation. No package to install, no configuration file to edit, no API key. The skill is a markdown file. The agent reads it.</p>
<h3 id="heading-verification">Verification</h3>
<p>Open Claude Code and type:</p>
<pre><code class="language-plaintext">/spec-writer test
</code></pre>
<p>If the skill is installed correctly, the agent will read the SKILL.md and produce a spec structure – even for "test" as input. You'll see the three sections and the Assumptions summary. If nothing happens, confirm that the <code>~/.claude/skills/spec-writer/SKILL.md</code> file exists.</p>
<h2 id="heading-how-to-write-your-first-spec">How to Write Your First Spec</h2>
<p>The invocation pattern is:</p>
<pre><code class="language-plaintext">/spec-writer [your feature description]
</code></pre>
<p>The description can be as rough as you'd write in a Slack message. The skill's job is to structure it. You don't need to be precise. Being precise is what the spec is for.</p>
<h3 id="heading-a-real-example">A Real Example</h3>
<p>Here is a real invocation from the Foundation project, a federated knowledge base built on Cloudflare Workers:</p>
<pre><code class="language-plaintext">/spec-writer Add CLI capture for Claude Code sessions stored in ~/.claude/projects/
</code></pre>
<p>That's twelve words. No mention of the tech stack, no data model details, no authentication requirements, no error handling spec. All of that comes out in the spec.</p>
<p>Here is the abbreviated output:</p>
<p><strong>SPEC</strong></p>
<p><strong>One-line purpose:</strong> Developers can capture Claude Code session logs from their local filesystem into Foundation before they are deleted after 30 days.</p>
<p><strong>Users and use cases:</strong></p>
<ul>
<li><p>As a developer using Claude Code daily, I want my coding sessions captured automatically so that architectural decisions and debugging patterns don't disappear after 30 days.</p>
</li>
<li><p>As a Foundation user, I want CLI sessions and browser extension captures in the same knowledge base so that I have one place to query across all my AI interactions.</p>
</li>
</ul>
<p><strong>Acceptance criteria:</strong></p>
<pre><code class="language-plaintext">Given a developer with Claude Code sessions in ~/.claude/projects/
When they run the Foundation CLI capture command
Then a list of uncaptured sessions is displayed, sorted by most recent first
 
Given a session already in Foundation
When the developer runs capture again
Then that session does not appear in the review list
 
Given a malformed .jsonl file
When the capture command processes it
Then an error is logged for that file and processing continues on remaining sessions
</code></pre>
<p><strong>PLAN</strong></p>
<p><strong>Stack and architecture:</strong></p>
<pre><code class="language-plaintext">CLI capture runs locally — it needs to read the local filesystem, which
Cloudflare Workers cannot do. [ASSUMPTION: CLI capture is a local Node.js
or Bun script that calls the Foundation API to insert sessions, rather
than a Worker itself]
</code></pre>
<p><strong>API contracts:</strong></p>
<ul>
<li><p><code>POST /api/sessions/import</code> – accepts parsed session JSON, returns session ID</p>
</li>
<li><p>Returns 409 if session already exists</p>
</li>
<li><p>Triggers evaluator pipeline after successful insert</p>
</li>
</ul>
<p><strong>TASKS</strong></p>
<pre><code class="language-plaintext">## Task 1: .jsonl parser
 
**What to build:** A parser that reads a Claude Code .jsonl session file
and outputs Foundation's internal message format.
**Files likely affected:** cli/src/parsers/claudeCode.js (create)
**Acceptance criteria:**
1. Parses a valid .jsonl file into the correct message format
2. Handles malformed lines without crashing — logs and skips
3. Returns empty array for empty file
**Dependencies:** none
</code></pre>
<p><strong>Assumptions to review:</strong></p>
<pre><code class="language-plaintext">1. CLI capture is a local script calling the Foundation API, not a Worker
   Impact: HIGH
   Correct this if: you want a purely serverless approach
 
2. Manual curation before capture, not automatic bulk import
   Impact: HIGH
   Correct this if: you want automatic background capture
 
3. Session ID from .jsonl filename is the deduplication key
   Impact: MEDIUM
   Correct this if: session IDs are stored differently in your schema
 
4. No sensitive data scrubbing in v1
   Impact: MEDIUM
   Correct this if: your sessions contain credentials or keys
</code></pre>
<p>Twelve words in, four decisions surfaced immediately – three of which had real architectural implications.</p>
<p>The third assumption ("Session ID from .jsonl filename is the deduplication key") is the one that would have caused the most subtle bug. The agent would have implemented deduplication based on the filename and it would have worked until a session was renamed. The spec caught it before a line of code was written.</p>
<h2 id="heading-how-to-read-the-output">How to Read the Output</h2>
<p>The output is designed to be scanned for <code>[ASSUMPTION: ...]</code> tags first, read for the tasks second.</p>
<h3 id="heading-reading-the-assumptions">Reading the Assumptions</h3>
<p>Every <code>[ASSUMPTION: ...]</code> tag marks a place where the agent filled in something you didn't specify. Your job is to go through the Assumptions summary and decide for each one:</p>
<ul>
<li><p><strong>Correct</strong>: the assumption is right, leave it</p>
</li>
<li><p><strong>Override</strong>: the assumption is wrong, restate it and re-run the spec</p>
</li>
<li><p><strong>Defer</strong>: the assumption doesn't matter for this iteration, mark it and move on</p>
</li>
</ul>
<p>The impact rating tells you which assumptions to fix before you start coding. HIGH-impact assumptions affect architecture or data model. If they're wrong, fixing them requires rework. LOW-impact assumptions affect behavior details that are easy to change later.</p>
<h3 id="heading-reading-the-acceptance-criteria">Reading the Acceptance Criteria</h3>
<p>The acceptance criteria in Given/When/Then format are the most useful part of the spec for catching scope errors. Read each one and ask: is this actually what I want?</p>
<p>Criteria are binary by design. "Returns 401 when unauthenticated" is a criterion. "Works correctly" is not. If you find yourself reading a criterion and thinking "well, it depends", then that's a signal that the criterion is hiding an assumption. Restate it.</p>
<h3 id="heading-reading-the-tasks">Reading the Tasks</h3>
<p>The tasks are ordered and self-contained. Each task produces a verifiable change. Before you hand any task to an agent, check two things:</p>
<ol>
<li><p>Does the task have all the context it needs? If a task says "follow the existing auth pattern" and you haven't pointed the agent at your auth code, it will guess.</p>
</li>
<li><p>Does the acceptance criteria match what you'd actually test? If the criteria are vague, tighten them before the agent sees the task.</p>
</li>
</ol>
<h2 id="heading-how-to-hand-the-spec-to-your-agent">How to Hand the Spec to Your Agent</h2>
<p>The spec is context, not a prompt. When you start an agent session for a task, include the relevant spec sections alongside the task description.</p>
<p>For Task 1 from the example above, your agent session might open like this:</p>
<pre><code class="language-plaintext">Context:
- This is a federated knowledge base built on Cloudflare Workers, D1, and Vectorize
- Sessions are stored in ~/.claude/projects/ as .jsonl files
- The API runs at https://&lt;your-worker&gt;.workers.dev
 
Spec:
[paste the SPEC and PLAN sections]
 
Task:
[paste Task 1]
</code></pre>
<p>The context block is just an example. Replace it with your own project's tech stack, file locations, and API URL. The point is to give the agent the same context a new team member would need on day one.</p>
<p>The agent now has requirements, architecture context, and a single scoped task with binary acceptance criteria. It cannot guess the deduplication key incorrectly because the spec already resolved that assumption. It cannot skip error handling because the acceptance criteria explicitly require it.</p>
<p>This is the workflow the spec is designed for. The spec doesn't replace the agent. Rather, it removes the decisions from the agent's hands and puts them in yours, before the work starts.</p>
<h3 id="heading-saving-the-spec-for-later">Saving the Spec for Later</h3>
<p>If you want to move toward Spec-Anchored development – where the spec lives in the repository – save the output to a <code>specs/</code> directory in your project:</p>
<pre><code class="language-bash"># Create specs directory
mkdir -p specs
 
# Save your spec
# Paste the output into specs/cli-capture.md
</code></pre>
<p>When requirements change, update the spec and re-prompt the agent to realign the implementation. The spec becomes the source of truth, not the code comments.</p>
<h2 id="heading-where-to-go-next">Where to Go Next</h2>
<p>Try it on your next feature before you write a line of code. The assumptions it flags will tell you something about your feature you hadn't consciously decided yet – and correcting the HIGH-impact ones before you hand anything to an agent is the whole point. Skipping that step is the same as prompting directly.</p>
<p>If your project is growing, move toward Spec-Anchored. Save specs in your repository under <code>specs/</code>. When a new contributor joins or an agent starts a session cold, the specs give them the decisions that got made without requiring them to reverse-engineer the code.</p>
<p>The strongest ongoing challenge to this workflow is Gabriella Gonzalez's argument that detailed specs become code. If your specs are getting implementation-specific, you've crossed a line. Pull back to decisions – "only authenticated users can trigger this" – and leave implementation to the agent. The spec's job is to name what the agent would have guessed wrong, not to write the feature in prose.</p>
<p>The Agent Skills standard now works across Claude Code, GitHub Copilot, Cursor, and Gemini CLI. The spec-writer repo is at <a href="https://github.com/dannwaneri/spec-writer">github.com/dannwaneri/spec-writer</a>.</p>
<p>The irony of spending 64% of a Claude budget building a token-efficiency tool is real. But the spec surfaced four decisions on a twelve-word prompt. The fourth one – the deduplication key assumption – would have produced a bug that worked perfectly until a session got renamed.</p>
<p>That's not a hallucination. That's the agent being exactly as helpful as the prompt allowed.</p>
<p>The spec is how you raise the ceiling on what "helpful" means.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
