Typst Rails

CI Gem Version License: MIT

TypstRails provides helpers for using Typst typesetting system with Ruby applications. Generate high-quality PDFs from Typst templates with seamless framework integration for Rails, Rage, and Sinatra. TypstRails can be used with either the typst gem or with a typst CLI.

Installation

Add this line to your application's Gemfile:

gem 'typst-rails'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install typst-rails

Rails Usage

.typ templates are automatically registered and work just like ERB templates:

# app/controllers/reports_controller.rb
class ReportsController < ApplicationController
  def monthly
    @title = "Monthly Report"
    @author = "Durable Systems Inc."

    respond_to do |format|
      format.pdf { render template: "reports/monthly" }
    end
  end
end
<%# app/views/reports/monthly.typ %>
<% data = { title: @title, author: @author } %>
#let data = json("typst_data.json")

= #data.title

Author: #data.author

Generated on <%= Time.now.strftime("%Y-%m-%d") %>

Rage Integration

Rage support is automatically enabled when the Rage framework is detected. Template integration follows Rage's conventions.

Sinatra Integration

When Sinatra is detected, a typst helper method is available:

require 'sinatra'
require 'typst_rails'

get '/report.pdf' do
  typst 'templates/report.typ', {
    title: "Monthly Report",
    author: "Durable Systems Inc."
  }
end

Helpers

The gem provides several helper methods for working with Typst templates:

Text Escaping

escape_typst("Price: $100")  # => "Price: \\$100"
escape_typst("Hello #world") # => "Hello \\#world"

HTML to Markdown/Typst Conversion

# Convert HTML to Markdown
html_to_markdown("<h1>Title</h1><p>Content</p>")
# => "# Title\n\nContent"

# Convert HTML directly to Typst
html_to_typst("<h1>Title</h1>")
# => "= Title\n\n"

# Sanitize HTML before conversion
sanitize_html('<script>alert("xss")</script><p>Safe</p>')
# => "<p>Safe</p>"

Markdown Conversion

# Convert Markdown to Typst syntax
markdown_to_typst("# Title\n## Subtitle")
# => "= Title\n== Subtitle"

# Include external Markdown files
include_markdown("./content.md")
# Reads and converts the file to Typst syntax

URL Encoding

url_encode("hello world") # => "hello+world"

These helpers are available in:

  • ERB templates (Rails) - use directly: <%= escape_typst(@text) %>
  • Plain Ruby code - include TypstRails::Helpers module
  • The renderer includes these helpers automatically

Configuration

TypstRails.configure do |config|
  config.typst_executable_path = "/custom/path/to/typst"
  config.default_root_path = Rails.root.join("app", "assets", "typst")
end

Compilation Backends

TypstRails compiles documents through a pluggable backend. Two backends ship with the gem:

  • :cli shells out to the typst executable. This is the original approach and requires Typst to be installed separately and available on PATH.

  • :gem uses the typst RubyGem, a native extension that compiles in-process—no subprocess or separate Typst install required. Add it to your Gemfile to enable it:

    gem "typst"
    

By default (config.backend = :auto), TypstRails prefers the typst gem when it's installed and falls back to the CLI otherwise. Force a specific backend if you need to:

TypstRails.configure do |config|
  config.backend = :gem # or :cli
end

You can also register your own backend—for example, to compile against a remote Typst service:

class MyRemoteBackend < TypstRails::Backends::Base
  def available?
    true
  end

  def compile(typ_path, root_dir)
    # ... return PDF bytes, or raise TypstRails::Error on failure
  end
end

TypstRails::Backends::Registry.register(:my_remote, MyRemoteBackend.new)
TypstRails.configure { |config| config.backend = :my_remote }

Testing

The gem includes comprehensive testing at multiple levels:

Unit Tests

Run the unit test suite:

bundle exec rake test

Tests cover:

  • Framework detection (Rails, Rage, Sinatra)
  • Core renderer functionality
  • Error handling and edge cases
  • All helper methods
  • Input validation and defensive programming

End-to-End Tests

Requires Typst to be installed.

The E2E tests process real ERB templates using all helpers and compile them with Typst to generate actual PDFs:

# Run E2E tests
bundle exec rake e2e

# Run all tests (unit + E2E)
bundle exec rake test_all

# Clean up generated test outputs
bundle exec rake clean_e2e

E2E tests verify:

  • Text escaping with real Typst special characters
  • HTML to Typst conversion with actual compilation
  • Markdown to Typst syntax transformation
  • External Markdown file inclusion
  • Complex real-world templates with multiple helpers

See e2e-tests/README.md for detailed documentation.

Docker End-to-End Tests

Requires Docker.

Verifies backend auto-detection (:cli vs :gem) across isolated container environments, and that the gem works correctly when built and installed like a real release rather than loaded from the working tree:

bundle exec rake e2e:docker

See e2e-docker/README.md for detailed documentation.

Code Coverage

bundle exec rake test
# Coverage report will be in coverage/index.html
open coverage/index.html

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests (note: currently, the gem has a basic structure and tests will be expanded with functionality). You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/durable-oss/typst-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.