NetSuite PDF/HTML Template Customization Guide

Every invoice, packing slip, and purchase order that leaves your NetSuite account is a representation of your brand. The default templates look generic at best and unprofessional at worst—plain text,...

10 min read

Disclosure: NetSuiteForge may earn a commission through affiliate links in this article. We only recommend solutions we've personally implemented for ecommerce clients. Our editorial process is independent, and our recommendations are based on hands-on experience.

NetSuite PDF/HTML Template Customization Guide

Every invoice, packing slip, and purchase order that leaves your NetSuite account is a representation of your brand. The default templates look generic at best and unprofessional at worst—plain text, no logo, basic formatting that screams "we just implemented new software."

For ecommerce brands, transaction templates matter even more than traditional businesses because your customers see them: order confirmations, invoices, packing slips in the box, and credit memos for returns. These documents are brand touchpoints, and they should look the part.

NetSuite uses FreeMarker (an open-source Java templating engine) to generate PDF and HTML documents from transaction data. The system is powerful but has a steep learning curve—the documentation is scattered, the debugging experience is painful, and common formatting issues waste hours if you don't know the workarounds.

This guide covers everything you need to customize NetSuite PDF/HTML templates for ecommerce, from FreeMarker basics through branded invoice templates, packing slip design, and solutions for the most common formatting problems.

Key Takeaways

  • NetSuite uses the FreeMarker templating language for PDF/HTML generation—learning basic FreeMarker syntax is essential for template customization
  • Advanced PDF/HTML Templates must be enabled before you can customize transaction documents (Setup → Company → Enable Features → SuiteCloud → Advanced PDF/HTML Templates)
  • The most common ecommerce templates to customize are invoices, packing slips, sales order confirmations, and credit memos
  • Image embedding, table formatting, and page breaks are the three areas where most customization time is spent
  • Test templates with real transaction data before deploying—edge cases (long product names, many line items, international addresses) break layouts

How Do You Get Started with Template Customization?

Enabling Advanced Templates

  1. Navigate to Setup → Company → Enable Features
  2. Go to the SuiteCloud tab
  3. Check Advanced PDF/HTML Templates
  4. Save

Once enabled, you can create and edit templates under Customization → Forms → Advanced PDF/HTML Templates.

Understanding the Template Architecture

NetSuite templates have three layers:

1. HTML/CSS: The structure and styling of the document 2. FreeMarker: Dynamic data insertion and logic (loops, conditionals) 3. BFO (Big Faceless Organization) PDF renderer: Converts HTML to PDF

The BFO renderer is important because it doesn't support all CSS features. Flexbox, Grid, and many modern CSS properties don't work. You're essentially designing for a limited HTML-to-PDF converter that supports roughly CSS 2.1 with some CSS3 features.

FreeMarker Basics

FreeMarker uses ${} for variable output and <#...> for directives:

Variable output:

${record.tranid}           <!-- Transaction number -->
${record.entity}           <!-- Customer name -->
${record.trandate}         <!-- Transaction date -->
${record.total}            <!-- Total amount -->

Conditional logic:

<#if record.shipmethod?has_content>
  Shipping: ${record.shipmethod}
</#if>

Looping through line items:

<#list record.item as item>
  <tr>
    <td>${item.item}</td>
    <td>${item.quantity}</td>
    <td>${item.rate}</td>
    <td>${item.amount}</td>
  </tr>
</#list>

Null handling (critical—unhandled nulls crash templates):

${record.memo!""}          <!-- Output empty string if memo is null -->
${record.custbody_field!"N/A"}  <!-- Output "N/A" if custom field is null -->

Common FreeMarker Gotchas

  1. Null values crash the template. Always use the !"" or ! default operator for fields that might be empty.
  2. Number formatting: Use ${item.amount?string(",##0.00")} to format numbers with commas and 2 decimal places.
  3. Date formatting: Use ${record.trandate?string("MM/dd/yyyy")} for US date format.
  4. HTML encoding: Use ${record.memo?html} to escape HTML characters in user-entered text.

How Do You Create a Branded Invoice Template?

Starting with the Default Template

Rather than building from scratch, copy the default template and modify it:

  1. Navigate to Customization → Forms → Advanced PDF/HTML Templates
  2. Find the default Invoice template
  3. Click Customize to create a copy
  4. Name it (e.g., "Branded Invoice - [Your Brand]")

Adding Your Logo

Image embedding in NetSuite templates is one of the most common customization tasks—and one of the most frustrating.

Method 1: File Cabinet URL (recommended)

  1. Upload your logo to Documents → Files → File Cabinet
  2. Get the file's Internal ID
  3. Reference it in the template:
<img src="${nsfont.src('your-logo.png')}" style="width: 200px; height: auto;" />

Or using the File Cabinet URL directly:

<img src="https://system.netsuite.com/core/media/media.nl?id=INTERNAL_ID&c=ACCOUNT_ID&h=FILE_HASH" 
     style="width: 200px;" />

Method 2: Base64 encoded image For logos that must always render (even offline):

<img src="data:image/png;base64,iVBORw0KGgo..." style="width: 200px;" />

Convert your logo to base64 using an online tool. This makes the template file larger but eliminates external URL dependencies.

Troubleshooting logo display:

  • If the logo doesn't appear in the PDF, check the file permissions in File Cabinet (must be "Available Without Login")
  • Base64 encoding is the most reliable method for PDF generation
  • Keep logos under 500KB for fast rendering

Invoice Layout Structure

A professional ecommerce invoice template structure:

<table style="width: 100%;">
  <!-- Header: Logo + Company Info + Invoice Number -->
  <tr>
    <td style="width: 50%;"><img src="..." /></td>
    <td style="width: 50%; text-align: right;">
      <strong>Invoice #${record.tranid}</strong><br/>
      Date: ${record.trandate}<br/>
      Due Date: ${record.duedate!""}
    </td>
  </tr>
</table>

<!-- Bill To / Ship To -->
<table style="width: 100%; margin-top: 20px;">
  <tr>
    <td style="width: 50%;">
      <strong>Bill To:</strong><br/>
      ${record.billaddress?html}
    </td>
    <td style="width: 50%;">
      <strong>Ship To:</strong><br/>
      ${record.shipaddress?html}
    </td>
  </tr>
</table>

<!-- Line Items -->
<table style="width: 100%; margin-top: 20px; border-collapse: collapse;">
  <thead>
    <tr style="background-color: #f0f0f0;">
      <th style="padding: 8px; text-align: left;">Item</th>
      <th style="padding: 8px; text-align: center;">Qty</th>
      <th style="padding: 8px; text-align: right;">Price</th>
      <th style="padding: 8px; text-align: right;">Amount</th>
    </tr>
  </thead>
  <tbody>
    <#list record.item as item>
    <tr>
      <td style="padding: 8px; border-bottom: 1px solid #eee;">${item.item}</td>
      <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: center;">${item.quantity}</td>
      <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${item.rate}</td>
      <td style="padding: 8px; border-bottom: 1px solid #eee; text-align: right;">${item.amount}</td>
    </tr>
    </#list>
  </tbody>
</table>

<!-- Totals -->
<table style="width: 40%; margin-left: auto; margin-top: 10px;">
  <tr><td>Subtotal:</td><td style="text-align: right;">${record.subtotal}</td></tr>
  <tr><td>Tax:</td><td style="text-align: right;">${record.taxtotal}</td></tr>
  <tr><td>Shipping:</td><td style="text-align: right;">${record.shippingcost!"0.00"}</td></tr>
  <tr style="font-weight: bold;">
    <td>Total:</td><td style="text-align: right;">${record.total}</td>
  </tr>
</table>

Adding Brand Colors

Use inline CSS with your brand colors:

<style>
  .header { background-color: #1a365d; color: white; padding: 20px; }
  .accent { color: #2b6cb0; }
  .total-row { background-color: #ebf4ff; }
</style>

Important: The BFO renderer has limited CSS support. Stick with:

  • Inline styles (most reliable)
  • Basic properties: color, background-color, font-size, font-weight, padding, margin, border
  • Table-based layouts (not flexbox or grid)
  • Avoid: float, position, flexbox, grid, box-shadow, border-radius (limited support)

How Do You Create Ecommerce Packing Slips?

Packing slips are the document your warehouse team uses to pick and pack orders, and the document your customer sees when they open the box. They need to serve both purposes.

Packing Slip Design Principles

For warehouse (picking):

  • Large, clear item names and SKUs
  • Prominent quantities
  • Bin/location information (if using WMS)
  • Order number and shipping method clearly visible

For customer (in-box):

  • Branded header with logo
  • No pricing information (standard for ecommerce packing slips)
  • Return instructions
  • Customer service contact
  • Optional: promotional message or coupon code

Hiding Prices on Packing Slips

Ecommerce packing slips typically don't show prices (the customer already has a receipt):

<!-- Only show item and quantity, no pricing -->
<#list record.item as item>
<tr>
  <td>${item.item}</td>
  <td>${item.description!""}</td>
  <td style="text-align: center;">${item.quantity}</td>
</tr>
</#list>

Adding Return Instructions

Add a footer section to your packing slip template:

<div style="margin-top: 30px; padding: 15px; border: 1px solid #ddd; font-size: 10px;">
  <strong>Returns & Exchanges</strong><br/>
  To initiate a return, visit yourstore.com/returns or email returns@yourstore.com<br/>
  Please include your order number: ${record.tranid}<br/>
  Returns accepted within 30 days of delivery.
</div>

What Are the Most Common Template Formatting Issues?

Issue 1: Page Breaks in the Middle of Tables

Problem: Long invoices with many line items split awkwardly across pages, sometimes cutting a row in half.

Fix:

<tr style="page-break-inside: avoid;">
  <!-- row content -->
</tr>

Or force a page break before a section:

<div style="page-break-before: always;">
  <!-- This section starts on a new page -->
</div>

Issue 2: Images Not Rendering in PDF

Problem: Logo or product images appear in HTML preview but not in the generated PDF.

Fix:

  • Ensure images are uploaded to File Cabinet with "Available Without Login" checked
  • Use absolute URLs, not relative paths
  • Consider base64 encoding for critical images
  • Keep image files under 500KB
  • Use PNG or JPEG format (SVG has limited support)

Issue 3: Fonts Not Displaying Correctly

Problem: Custom fonts don't render in PDF.

Fix: The BFO renderer supports a limited set of fonts. Safe choices:

  • Helvetica / Arial
  • Times / Times New Roman
  • Courier
  • Symbol
  • ZapfDingbats

For custom fonts, you can embed them, but it's complex. Most brands use Helvetica or Arial for PDF templates and save custom fonts for web-only communications.

Issue 4: Currency Formatting

Problem: Currency amounts display without proper formatting.

Fix:

${item.amount?string(",##0.00")}     <!-- 1,234.56 -->
$${record.total?string(",##0.00")}   <!-- $1,234.56 -->

For multi-currency:

${record.currencysymbol}${record.total?string(",##0.00")}

Issue 5: Long Product Names Breaking Layout

Problem: Long item descriptions overflow table cells and break the layout.

Fix:

<td style="word-wrap: break-word; max-width: 300px; overflow: hidden;">
  ${item.description!""}
</td>

Or truncate long descriptions:

<#if (item.description!"")?length gt 80>
  ${(item.description!"")?substring(0, 80)}...
<#else>
  ${item.description!""}
</#if>

Issue 6: Address Formatting for International Orders

Problem: International addresses don't format correctly (different country formats).

Fix: Use NetSuite's built-in address formatting:

${record.billaddress?html}

This outputs the address formatted for the country. For custom formatting:

${record.billaddressee!""}<br/>
${record.billaddr1!""}<br/>
<#if record.billaddr2?has_content>${record.billaddr2}<br/></#if>
${record.billcity!""}, ${record.billstate!""} ${record.billzip!""}<br/>
${record.billcountry!""}

How Do You Test and Deploy Templates?

Testing Process

  1. Preview with sample data: In the template editor, click "Preview" and select a real transaction to render
  2. Test edge cases: Preview with transactions that have:
    • 1 line item (minimal)
    • 50+ line items (multi-page)
    • Long product names
    • International addresses
    • Discounted items
    • Tax-exempt transactions
  3. Test PDF generation: Click "Download PDF" and check the actual PDF output (sometimes different from HTML preview)
  4. Test email delivery: Send a test email with the template attached to verify it renders correctly in email

Deploying Templates

  1. Create the template under Customization → Forms → Advanced PDF/HTML Templates
  2. Assign the template to the appropriate transaction form:
    • Navigate to Customization → Forms → Transaction Forms
    • Edit the form for the relevant transaction type
    • Under the "Printing & Email" tab, select your custom template
  3. Test in production by previewing a live transaction before emailing to customers

Template Version Control

NetSuite doesn't have built-in version control for templates. Best practices:

  • Keep a copy of each template version in your local repository or document storage
  • Name templates with version numbers: "Invoice Template v2.3"
  • Document changes in a changelog (what changed and why)
  • Keep the previous version active as a fallback until the new version is verified

Frequently Asked Questions

Can I use CSS Grid or Flexbox in NetSuite PDF templates?

No. The BFO PDF renderer supports approximately CSS 2.1 with some CSS3 additions. Use table-based layouts for reliable PDF rendering. HTML preview may show flexbox/grid correctly, but the PDF output will break.

How do I add a barcode to a packing slip?

Use a barcode font or generate barcode images. For Code 128 barcodes, upload a barcode font to the File Cabinet and reference it in your template. Alternatively, use a barcode generation service URL that returns an image based on the value passed.

Can I create different templates for different customers?

Yes. You can assign templates to custom forms, and custom forms can be assigned by customer category, sales channel, or subsidiary. For example, wholesale invoices can use a different template than DTC invoices.

How do I include product images on invoices or packing slips?

If your items have images uploaded to the File Cabinet, reference them in the template:

<#if item.itemimageurl?has_content>
  <img src="${item.itemimageurl}" style="width: 50px; height: 50px;" />
</#if>

Note: This slows PDF generation significantly if you have many line items. Use small thumbnail images (under 50KB each).

Is there a way to preview template changes without affecting live documents?

Yes. Create a copy of your template, make changes on the copy, and preview it with real transactions. Only assign the copy to your transaction forms once you're satisfied with the result. The original template continues to serve live documents until you switch.

Can I generate templates in multiple languages?

Yes. Use FreeMarker conditionals with the customer's language preference:

<#if record.entity.language == "fr_FR">
  Facture
<#else>
  Invoice
</#if>

For full multi-language support, consider creating separate templates per language and assigning them based on customer region.

Ready to Brand Your NetSuite Documents?

Professional, branded transaction documents elevate your ecommerce brand experience. Your invoices, packing slips, and order confirmations should look as polished as your website.

Start with your highest-volume document (usually the packing slip for ecommerce), get it right, then apply the same design language to invoices, credit memos, and purchase orders.

Take our free NetSuite readiness assessment → to evaluate your current template setup and get a prioritized list of documents to customize for your ecommerce brand.

Related Articles