Add a PDF download to an existing Rails page, or generate a PDF from a template. The examples use PDFCrowd's HTML to PDF API and work for invoices, reports, receipts, and other documents built from application data.
Set up the client
Add the Ruby client to your application:
bundle add pdfcrowd
You can run the examples below with the included demo credentials, or replace
them with your PDFCrowd username and API key.
Add a PDF download to an existing page
An existing controller action can display its usual HTML page and return a PDF when the user clicks a download button. Reuse the action's template, data preparation, and access checks.
Choose what to send to PDFCrowd:
- Rendered HTML uses content Rails has already prepared.
- The page URL loads the page through its normal address.
Convert rendered HTML
This example extends a document page. In config/routes.rb, make its route accept GET
and POST inside the existing Rails.application.routes.draw block:
match "/documents/:id", to: "documents#show", via: [:get, :post], constraints: { id: /\d+/ }, as: :document
Extend DocumentsController in app/controllers/documents_controller.rb.
get_document_for_current_user(params[:id]) represents your application's document lookup
and permission checks; substitute your own logic and keep the controller's existing access
restrictions. The template at app/views/documents/show.html.erb receives @document.
require "pdfcrowd" class DocumentsController < ApplicationController def show @document = get_document_for_current_user(params[:id]) if request.post? && params[:download_pdf] @pdf_base_url = Rails.application.config.x.pdf.base_url html = render_to_string( template: "documents/show", formats: [:html], layout: true ) client = Pdfcrowd::HtmlToPdfClient.new("demo", "demo") # Username, API key client.setContentViewportWidth("balanced") pdf = client.convertString(html) # Binary String send_data pdf, type: "application/pdf", disposition: "attachment", filename: "document-#{params[:id]}.pdf" return end render :show, formats: [:html] end end
Add this form to the ERB template, outside any existing form:
<%= form_with url: request.fullpath, method: :post, local: true, data: { turbo: false }, class: "pdfcrowd-remove" do %> <button type="submit" name="download_pdf" value="1">Download PDF</button> <% end %>
Keep Rails' CSRF protection enabled; form_with supplies the authenticity token.
local: true and data: { turbo: false } make this a normal browser submission, so the
browser handles the PDF attachment. The latter disables
Turbo interception for this form.
GET requests display the HTML page. Submitting the download button sends a POST, renders
the same template and layout, converts the HTML, and returns a PDF download. pdfcrowd-remove
hides the form in the PDF while leaving it visible on the webpage. The @pdf_base_url value
supports the CSS and image setup below.
The PDF stays in memory until conversion succeeds. send_data then sends the binary String
without a second HTML render. Rendering and conversion errors propagate to Rails' error
handler; keep detailed error pages disabled in production so private details remain server-side.
See SDK error handling for details.
To display the PDF in the browser, change attachment to inline. Customize
page size,
margins, or other
PDF options on the client before conversion.
Make CSS and images available
When you send rendered HTML to PDFCrowd, relative paths to CSS and images need a base URL so PDFCrowd can locate the files and include them in the PDF.
Check the asset URLs in the rendered HTML. If all URLs are complete, such as
https://cdn.your-site.example/assets/style.css, or the layout already provides a correctly
configured <base> element, no additional setup is needed.
For relative paths, add this inside your application class in config/application.rb:
config.x.pdf.base_url = "https://your-site.example/"
Replace https://your-site.example/ with your website's public URL, where PDFCrowd can
access the assets. Add this to the layout's <head>, before stylesheet links:
<% if @pdf_base_url.present? %> <base href="<%= @pdf_base_url %>"> <% end %>
The controller supplies @pdf_base_url only when rendering for conversion. ERB escapes the
attribute value. Use the existing <base> element if the layout already has one.
For example, with a base of https://your-site.example/reports/, images/logo.png resolves
to https://your-site.example/reports/images/logo.png. A path starting with /, such as
/assets/style.css, resolves from the site root. Keep the trailing slash when the base refers
to a directory, and use the directory that matches your relative paths.
The base URL does not upload files or grant access to private assets. For files that are only available on your server, see convert HTML with local assets.
Convert the page URL instead
For a page PDFCrowd can load through its normal address, use URL conversion. Remove the
@pdf_base_url assignment and render_to_string(...) call from the download branch, then
replace client.convertString(html) with:
pdf = client.convertUrl(request.original_url) # Binary String
Keep the client setup, settings, and send_data call. The URL preserves the current path
and query parameters. PDFCrowd makes a separate GET request, so the action returns HTML;
only the original POST triggers conversion. Your server must be able to handle that GET
while the POST waits for the PDF.
Use a URL that PDFCrowd can reach; localhost cannot reach your development machine.
Ensure request.original_url uses your public address, with the appropriate allowed-host
and proxy configuration.
The separate request does not inherit the user's Rails session. For protected pages, configure cookies or HTTP authentication as appropriate, or use rendered HTML from the original request. Use rendered HTML if application locking would make that GET wait for the POST to finish.
Generate a PDF from a template
You can also generate a PDF directly from a template and application data. Use an existing ERB template or a separate template designed for the document.
Add this class to app/services/pdf_renderer.rb. It renders the supplied template and layout
and returns the PDF as a binary String, using the same
asset configuration as the controller:
require "pdfcrowd" class PdfRenderer def self.render(template, context, layout: "application") html = ApplicationController.render( template: template, formats: [:html], layout: layout, assigns: context.merge( pdf_base_url: Rails.application.config.x.pdf.base_url ) ) client = Pdfcrowd::HtmlToPdfClient.new("demo", "demo") # Username, API key client.setContentViewportWidth("balanced") client.convertString(html) end end
For example, given an authorized document object and a template at
app/views/documents/pdf.html.erb, call:
pdf = PdfRenderer.render("documents/pdf", { document: document })
Pass all data the template needs in context, using symbol keys. Each key becomes an instance
variable: document: supplies @document. Use the returned bytes for a download, storage,
or email. Rendering and conversion errors propagate to the caller.
The helper uses the booted application's
renderer, so it also works
outside a controller action. It does not inherit the active request's user or session, or run
controller access checks. Supply authorized data and use a template and layout that do not
depend on request-only helpers or forms. Pass layout: "pdf" to use your own PDF layout,
or layout: false if the template contains a complete HTML document.
If the template uses absolute URL helpers such as document_url, configure the application's
Rails.application.routes.default_url_options with the public host and protocol. A <base>
element resolves relative URLs in the HTML; it does not configure Rails' URL helpers.
For more conversion inputs and PDF settings, see the Ruby examples and API reference.