Skip to main content

How to Generate Attractive PDF Files Using Visualforce in Salesforce

In plain words: You can turn any Salesforce data into a downloadable PDF document (like invoices, quotes, or customer reports) using Visualforce. By simply adding renderAs="pdf" to your <apex:page> tag and applying standard CSS, Salesforce's rendering engine automatically converts the HTML into a formatted PDF file.

Generating PDFs dynamically from CRM data is a common business requirement. Whether you need to generate professional quotes for clients, formatted invoices, or compliance reports, Visualforce remains the most reliable native tool in Salesforce for PDF generation. By combining standard Apex controllers with standard HTML and CSS, you can create pixel-perfect documents.

1. The Fundamentals of Visualforce PDF Generation

The Salesforce platform uses a specialized server-side rendering engine (Flying Saucer) to convert Visualforce markup into a PDF document. To trigger this engine, you only need to define two core components:

  • The renderAs="pdf" Attribute: Adding this to your root <apex:page> tag tells Salesforce to bypass standard HTML rendering and output a binary PDF file.
  • CSS for Print Media: Because you are designing for paper rather than a screen, you must use standard CSS block models, margins, and the @page directive to control page breaks and margins.
360 Visualforce PDF Architecture Card:
  • Trigger: <apex:page renderAs="pdf">
  • Styling Engine: Standard HTML/CSS (Salesforce Lightning Design System / SLDS is not supported in PDFs).
  • Data Binding: Connects to an Apex Controller or Standard Controller to loop through records using <apex:repeat>.
  • Delivery: Can be viewed in the browser, downloaded, or attached to a record using Apex (PageReference.getContentAsPDF()).

2. Creating the PDF Visualforce Page

Step-by-Step Implementation: Generating a Contact Report PDF
The following example creates a clean, styled table displaying a list of Contacts fetched from a custom Apex controller.
<!-- ContactReportPDF.page -->
<apex:page controller="PDFController" renderAs="pdf" applyHtmlTag="false" showHeader="false">
    <head>
        <style type="text/css">
            /* Define the printed page margins */
            @page {
                margin: 0.5in;
                size: letter;
            }
            
            body {
                font-family: Arial, sans-serif;
                font-size: 12px;
                color: #333333;
            }
            
            h1 {
                font-size: 24px;
                color: #006699;
                text-align: center;
                margin-bottom: 20px;
                border-bottom: 2px solid #006699;
                padding-bottom: 10px;
            }
            
            table {
                width: 100%;
                border-collapse: collapse;
                margin-bottom: 20px;
            }
            
            th, td {
                padding: 10px;
                border: 1px solid #cccccc;
            }
            
            th {
                background-color: #f2f2f2;
                font-weight: bold;
                text-align: left;
            }
            
            /* Zebra striping for better readability */
            tr:nth-child(even) {
                background-color: #fafafa;
            }
        </style>
    </head>
    
    <body>
        <h1>Customer Contact Report</h1>
        
        <table>
            <thead>
                <tr>
                    <th>Full Name</th>
                    <th>Email Address</th>
                    <th>Phone Number</th>
                </tr>
            </thead>
            <tbody>
                <!-- Loop through the records provided by the Apex controller -->
                <apex:repeat value="{!contacts}" var="contact">
                    <tr>
                        <td>{!contact.Name}</td>
                        <td>{!contact.Email}</td>
                        <td>{!contact.Phone}</td>
                    </tr>
                </apex:repeat>
            </tbody>
        </table>
    </body>
</apex:page>

3. Supplying Data via the Apex Controller

To populate the PDF, you must build the corresponding Apex controller (PDFController) that queries the database and passes the contacts collection to the Visualforce page.

public with sharing class PDFController {
    
    // Property accessed by the Visualforce page
    public List<Contact> contacts { get; set; }

    public PDFController() {
        // Query recent contacts to display in the PDF
        contacts = [SELECT Name, Email, Phone 
                    FROM Contact 
                    WITH USER_MODE
                    ORDER BY CreatedDate DESC 
                    LIMIT 20];
    }
}

4. Common Traps & Rendering Limitations

Design Trap: Using Advanced Modern CSS and SLDS
The Salesforce PDF rendering engine does not support modern CSS features like CSS Grid, Flexbox, or the Salesforce Lightning Design System (SLDS). If you attempt to use SLDS classes (slds-grid), the layout will break entirely in the generated PDF. You must use classic HTML tables and inline/block styling to construct your layouts.
Core Rule: Keep PDF Visualforce pages lightweight. Set applyHtmlTag="false" and showHeader="false" to strip out standard Salesforce wrapper code, and rely strictly on basic CSS and HTML tables for layout control.
  • Governor Limits: The rendered PDF file size cannot exceed 15 MB. Be mindful of querying large datasets or embedding massive high-resolution images.
  • Image Rendering: If you include logos or graphics, they must be stored in Salesforce as Static Resources. Reference them securely using the {!$Resource.LogoName} global variable.
  • Page Breaks: Use the CSS property page-break-inside: avoid; on table rows (<tr>) to prevent data rows from being awkwardly split across two PDF pages.

Summary

Generating PDF documents natively in Salesforce is straightforward using Visualforce's renderAs="pdf" capability. By coupling a basic Apex controller with clean HTML tables and standard CSS styling, you can automatically generate dynamic, branded reports, invoices, and quotes directly from your CRM data.