Ruby Guide
HTML to PDF in Ruby
Generate PDFs from HTML in Rails or Sinatra — without WickedPDF, PDFKit, or wkhtmltopdf. No system binary to install, no gem conflicts. Works anywhere Ruby runs.
Why not WickedPDF or PDFKit?
WickedPDF and PDFKit are wrappers around wkhtmltopdf — a binary that uses an old WebKit build from ~2012. It doesn't support Flexbox, CSS Grid, or modern fonts properly. You also need to install the wkhtmltopdf system binary in every environment: local, CI, staging, production, Docker. Version mismatches between environments are common.
Papyr uses modern Chromium. No system binary, no version drift, no layout surprises.
Plain Ruby
Works with just the standard library — no gems required.
require "net/http"
require "json"
def html_to_pdf(html)
uri = URI("https://api.getpapyr.dev/v1/render/pdf")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['PAPYR_API_KEY']}"
req["Content-Type"] = "application/json"
req.body = { html: html, options: { format: "A4" } }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
raise "PDF error: #{res.code}" unless res.is_a?(Net::HTTPSuccess)
res.body
end
# Save to disk
File.binwrite("output.pdf", html_to_pdf("<h1>Hello, PDF!</h1>"))Rails controller
Render your ERB template to a string, send to Papyr, stream back as a file download.
# app/controllers/invoices_controller.rb
require "net/http"
require "json"
class InvoicesController < ApplicationController
def pdf
invoice = Invoice.find(params[:id])
# Render your ERB/Haml template to an HTML string
html = render_to_string(
template: "invoices/pdf",
layout: "pdf",
locals: { invoice: invoice }
)
response = papyr_render(html)
send_data response,
filename: "invoice-#{invoice.number}.pdf",
type: "application/pdf",
disposition: "attachment"
end
private
def papyr_render(html)
uri = URI("https://api.getpapyr.dev/v1/render/pdf")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['PAPYR_API_KEY']}"
req["Content-Type"] = "application/json"
req.body = { html: html }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
raise "PDF generation failed" unless res.is_a?(Net::HTTPSuccess)
res.body
end
endRails + Faraday + Active Storage
Generate and attach the PDF directly to an Active Storage record — no temp files.
# Using Faraday gem (common in Rails apps)
require "faraday"
def html_to_pdf(html, options = {})
conn = Faraday.new("https://api.getpapyr.dev") do |f|
f.request :json
f.response :raise_error
end
response = conn.post("/v1/render/pdf") do |req|
req.headers["Authorization"] = "Bearer #{ENV['PAPYR_API_KEY']}"
req.body = { html: html, options: { format: "A4" }.merge(options) }
end
response.body
end
# Store in Active Storage / S3
pdf = html_to_pdf(html, landscape: true)
invoice.pdf_file.attach(
io: StringIO.new(pdf),
filename: "invoice-#{invoice.number}.pdf",
content_type: "application/pdf"
)Get your API key
Free tier: 100 PDFs/month. No credit card required.