Skip to main content
Pdfn supports three styling approaches. Choose based on your needs:
ApproachInstallBest for
Tailwind CSS@pdfn/tailwindRapid development, utility classes
CSS propNothing extraCustom stylesheets, design systems
Inline stylesNothing extraSimple templates, quick prototypes

Tailwind CSS

Install @pdfn/tailwind:
npm install @pdfn/tailwind
Wrap your Page content with the Tailwind component:
import { Document, Page } from '@pdfn/react';
import { Tailwind } from '@pdfn/tailwind';

export default function Invoice() {
  return (
    <Document title="Invoice">
      <Tailwind>
        <Page size="A4" margin="1in">
          <h1 className="text-2xl font-bold text-blue-600">Invoice #001</h1>
          <p className="text-gray-600 mt-2">Thank you for your business.</p>
          <div className="mt-8 p-4 bg-gray-100 rounded-lg">
            <p className="text-xl font-bold">Total: $148.00</p>
          </div>
        </Page>
      </Tailwind>
    </Document>
  );
}
Place Tailwind inside Document but it can wrap one or more Page components.

Custom theme

Customize your PDF theme in pdfn-templates/styles.css. This file is auto-detected and isolated from your app’s styles — your app and PDF templates never interfere with each other.
pdfn-templates/styles.css
@import "tailwindcss";

@theme {
  --font-inter: "Inter", var(--font-sans);
  --color-brand: #007bff;
}
Use your custom values:
<h1 className="font-inter text-brand">Styled with custom theme</h1>

Edge runtime (Vercel Edge, Cloudflare Workers)

Edge runtimes don’t have filesystem access, so Tailwind must be pre-compiled at build time. For Next.js:
npm install @pdfn/next
next.config.ts
import { withPdfn } from '@pdfn/next';

const nextConfig = { /* your existing config */ };

export default withPdfn()(nextConfig);
For Vite:
npm install @pdfn/vite
vite.config.ts
import { defineConfig } from 'vite';
import { pdfn } from '@pdfn/vite';

export default defineConfig({
  plugins: [pdfn()]
});

CSS prop

Add custom CSS directly via the css prop on Document. No extra packages needed.
import { Document, Page } from '@pdfn/react';

export default function Invoice() {
  return (
    <Document
      title="Invoice"
      css={`
        .header {
          font-size: 24px;
          font-weight: bold;
          border-bottom: 2px solid #007bff;
          padding-bottom: 16px;
        }
        .total {
          font-size: 20px;
          font-weight: bold;
          background: #f5f5f5;
          padding: 16px;
          border-radius: 8px;
        }
      `}
    >
      <Page size="A4" margin="1in">
        <h1 className="header">Invoice #001</h1>
        <p className="total">Total: $148.00</p>
      </Page>
    </Document>
  );
}

Inline styles

Use React’s style prop for styling without extra dependencies:
import { Document, Page } from '@pdfn/react';

export default function Invoice() {
  return (
    <Document title="Invoice">
      <Page size="A4" margin="1in">
        <h1 style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 16 }}>
          Invoice #001
        </h1>
        <p style={{ color: '#666', lineHeight: 1.5 }}>
          Thank you for your business.
        </p>
      </Page>
    </Document>
  );
}

Combining approaches

You can mix all three approaches. Styles are applied in this order (later wins):
  1. Base pdfn styles
  2. Tailwind CSS classes
  3. Document css prop
  4. Inline style prop
<Document css={`.highlight { background: yellow; }`}>
  <Tailwind>
    <Page>
      {/* Tailwind class */}
      <h1 className="text-2xl font-bold">Title</h1>

      {/* Custom CSS class */}
      <p className="highlight">Highlighted text</p>

      {/* Inline style (highest priority) */}
      <p style={{ color: 'red' }}>Red text</p>
    </Page>
  </Tailwind>
</Document>

Fonts

Load custom fonts via the fonts prop on Document. Pdfn supports Google Fonts and local font files.

Google Fonts

Pass font names as strings, or configure weights and styles:
import { Document, Page } from '@pdfn/react';

// Simple — default weights (400, 500, 600, 700)
<Document fonts={["Inter", "Roboto Mono"]}>
  <Page size="A4">
    <h1 style={{ fontFamily: 'Inter' }}>Styled heading</h1>
    <code style={{ fontFamily: 'Roboto Mono' }}>console.log('hello')</code>
  </Page>
</Document>

// With specific weights
<Document fonts={[{ family: "Inter", weights: [400, 700] }]}>
FieldTypeDefaultDescription
familystringFont family name (must match Google Fonts)
weightsnumber[][400, 500, 600, 700]Font weights to include
style"normal" | "italic""normal"Font style

Local fonts

Embed custom font files directly. The font is base64-encoded into the PDF.
<Document fonts={[
  { family: "CustomFont", src: "./fonts/custom.woff2", weight: 400 },
  { family: "CustomFont", src: "./fonts/custom-bold.woff2", weight: 700 },
]}>
  <Page size="A4">
    <p style={{ fontFamily: 'CustomFont' }}>Custom font text</p>
  </Page>
</Document>
FieldTypeDefaultDescription
familystringFont family name (use in CSS)
srcstringPath to font file (.woff2, .woff, .ttf, .otf)
weightnumber400Font weight
style"normal" | "italic""normal"Font style
Local fonts require filesystem access and won’t work on edge runtimes (Vercel Edge, Cloudflare Workers). Use Google Fonts for edge compatibility.

Images

Images must be accessible when the PDF is generated. Since pdfn Cloud renders on remote servers, local filesystem paths won’t work in production.

Next.js public folder

Put images in your public/ folder and reference them with absolute URLs. These deploy with your app and work everywhere.
function Invoice() {
  const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';

  return (
    <Document title="Invoice">
      <Page size="A4">
        <img src={`${baseUrl}/logo.png`} alt="Company logo" />
      </Page>
    </Document>
  );
}

CDN-hosted images

For large assets or images shared across projects, host them on a CDN (Cloudflare R2, AWS S3, Vercel Blob).
<img src="https://cdn.example.com/assets/logo.png" alt="Company logo" />

Base64 data URLs

For small images like logos or icons (under 10KB), embed them directly.
const logo = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...';

function Invoice() {
  return (
    <Document title="Invoice">
      <Page size="A4">
        <img src={logo} alt="Company logo" />
      </Page>
    </Document>
  );
}
Local image paths (like ./images/logo.png) work during development but fail in production. Always use absolute URLs or base64.

PDF-specific CSS notes

Some CSS properties behave differently in PDFs:
PropertyBehavior
Flexbox & GridFully supported
box-shadowWorks, but avoid blur > 10px (slow rendering)
position: fixedBehaves like absolute (no viewport in PDFs)
vh / vw unitsRelative to page size, not browser viewport
@media printRespected — use for PDF-specific styles

Next steps