Being able to download blocks as a PDF?

4 replies
Last updated: Nov 29, 2025
Wondering if anyone has thought of this. Being able to download blocks as a PDF? My client is asking for this feature, so it would be cool to hear insights if anyone has done something similar.
AI Update

Great question! Generating PDFs from Portable Text blocks is definitely doable, and several people in the community have tackled this. Here's how you can approach it:

The General Approach

The key is to use Portable Text serializers to transform your blocks into an intermediate format that a PDF library can work with. You have a few options:

The modern approach is to use Sanity Functions - serverless functions that can process your content. You could create a function that:

  • Receives a document ID or specific blocks
  • Serializes the Portable Text to HTML
  • Uses a PDF generation library like puppeteer, playwright, or @react-pdf/renderer
  • Returns the PDF file or uploads it to your storage

This keeps everything within the Sanity ecosystem and scales automatically without needing external hosting.

2. Server-Side Generation

If you're running your own backend, you can create an API endpoint that:

  • Queries the content from Sanity using GROQ
  • Serializes Portable Text to HTML using @portabletext/to-html or custom serializers
  • Converts HTML to PDF using libraries like:
    • pdfmake (works with JSON structure)
    • puppeteer or playwright (renders HTML to PDF)
    • jsPDF with html2canvas
    • @react-pdf/renderer (if you want React-based PDF layouts)

3. Example Workflow

// Serialize Portable Text to HTML
import { toHTML } from '@portabletext/to-html'

const html = toHTML(blocks, {
  components: {
    types: {
      image: ({value}) => `<img src="${value.asset.url}" />`,
      // Add custom handlers for your block types
    }
  }
})

// Then use puppeteer or similar
import puppeteer from 'puppeteer'

const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.setContent(html)
const pdf = await page.pdf({ format: 'A4' })

Considerations

  • Styling: Make sure your serializers include proper CSS for the PDF layout
  • Images: Handle image URLs properly (they need to be accessible to the PDF generator)
  • Custom blocks: You'll need serializers for any custom block types you have
  • Performance: PDF generation can be resource-intensive, so consider caching or background processing

The ultimate guide for customizing Portable Text has great examples of custom serializers that would be helpful here.

Would love to hear what approach you end up taking! If you share more about your block structure, the community might have more specific suggestions.

Show original thread
4 replies

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?