You give it HTML and it gives back an image!
Parses HTML/CSS, fetches nested resources, renders an image. No browser, no fuss.
Perfect for OpenGraph images - stop losing clicks to boring links by adding a rich image preview that showcases your content.
gem install himg
himg screenshot path/to/your.html screenshot.png
himg screenshot https://himg.jamedjo.co.uk himg.png --width=1024 --verbose --no-truncate
echo '<h1>Hello Image</h1>' | himg screenshot --stdin output.pngpng = Himg.render("<!DOCTYPE html><body style='background:blue;'></body>")
File.open("blue.png", "wb"){|f| f.write(png) }png = Himg.render("<!DOCTYPE html><h1>Snapshot</h1>", width: 89, height: 5, truncate: false)
File.open("dynamic_height.png", "wb"){|f| f.write(png) }Simply add a show.himg.erb!
<div><%= @username %></div>OpenGraph tags let messenger apps and social media sites know to use your generated image as a thumbnail card for your website.
<meta property="og:title" content="<%= @user.username %>" />
<meta property="og:description" content="<%= @user.tagline %>" />
<meta property="og:image" content="<%= user_url(@user.username, format: :png) %>" />Install the gem and add to the application's Gemfile by executing:
bundle add himg| Option | Description | Type | Default |
|---|---|---|---|
| width | Sets the width of the rendered content. | integer | 720 |
| height | Sets the desired height of the rendered output. | integer | 405 |
| truncate | Keeps the image height fixed instead of expanding to include the full page | bool | true |
| verbose | Enables detailed logging for debugging and profiling. | bool | false |
| base_url | Where relative paths are relative to for linked resources (stylesheets, images, fonts, etc) | string | nil |
| disable_fetch | Disables fetching linked resources from disk and network | bool | false |
| fetch_timeout | Timeout in seconds for fetching resources | float | 10.0 |
| gpu | Use GPU renderer instead of CPU renderer | bool | false |
| http_headers | Headers sent when the CLI fetches the SOURCE HTML (CLI only) | hash | nil |
| stdin | Read HTML content from stdin instead of a file (CLI only) | bool | false |
Options can be set at a controller level using the himg_config helper method:
class UsersController < ActionController::Base
himg_config(verbose: true)
endThese can be overridden at a view level:
class UsersController < ActionController::Base
def show
himg_config(width: params[:w]) if params[:w]
@user = User.new
endIf you prefer you could also use render himg: "<div>My Data</div>" instead, but should be careful with untrusted input if constructing HTML manually.
Options can then be passed directly to the manual render:
render himg: '<!DOCTYPE html>', truncate: falseAlternatively you can pass in options which have been set with himg_config:
render himg: '<!DOCTYPE html>', config: himg_configTo be explicit in the controller you can also use respond_to style:
respond_to do |format|
format.html
format.himg
endYou can also use this combined with a manual render:
respond_to do |format|
format.html
format.himg { render himg: '<h1 style="text-align: center;">Recent Users</h1>' }
format.png { render himg: '<div>For .png URLs</div>' }
endYou can cache images to save re-generating them for later requests.
To serve the cached response you'll need to use send_data to send the png data as binary content.
format.png do
cache_key = ["opengraph-your-resource", id_or_data].join("/")
cached_image = Rails.cache.fetch(cache_key, expires_in: 30.days) do
render_to_string(template: 'your_resource/show', formats: [:himg])
end
send_data cached_image, type: 'image/png', disposition: 'inline'
endYou can also speed up the render time by minimizing resources to fetch over the network, for example installing fonts locally or ensuring background images are read from disk as a file rather than fetched over http.
Himg supports a large subset of the HTML and CSS you'd need to get by, but not all elements or properties are supported.
For a full list, see our snapshot of the blitz.is status page.
If something you'd like supported is missing and is available upstream on Blitz, please open an issue.
| HTML | Status |
|---|---|
Generic HTML5 elements |
✅ Supported |
<style> |
✅ Supported |
<link rel="stylesheet"> |
✅ Supported |
<link rel="icon"> |
❌ Not supported |
<img src=""> |
✅ Supported |
<img srcset=""> |
❌ Not supported |
<picture> |
❌ Not supported |
<svg> |
|
<h1>-<h6> |
✅ Supported |
<ul>/<ol> |
✅ Supported |
<i>/<em> |
✅ Supported |
<b>/<strong> |
✅ Supported |
<u> |
✅ Supported |
<center> |
✅ Supported |
<pre>/<blockquote> |
✅ Supported |
<a> |
✅ Supported |
<br> |
✅ Supported |
<hr> |
❌ Not supported |
<details>/<summary> |
❌ Not supported |
<table> |
|
Form Controls |
| CSS | Status |
|---|---|
display:inline, display:block, display:inline-block |
✅ Supported |
display:none |
✅ Supported |
display:flex |
✅ Supported |
display:grid |
|
display:table |
|
display:contents |
❌ Not supported |
position:relative, position:absolute |
✅ Supported |
position:static, position:fixed, position:sticky |
❌ Not supported |
overflow |
|
z-index |
|
box-sizing |
✅ Supported |
float |
❌ Not supported |
content (::before / ::after) |
|
opacity, visibility |
✅ Supported |
width, height |
✅ Supported |
min-width, max-width, min-height, max-height |
✅ Supported |
padding, margin, gap |
✅ Supported |
border |
|
@font-face, font-size, font-family |
✅ Supported |
font-weight, font-style, font-stretch |
✅ Supported |
font-display, font-variant, font-feature-settings |
❌ Not supported |
color, text-align, line-height, text-decoration |
✅ Supported |
letter-spacing, overlap-wrap, word-wrap, word-break |
✅ Supported |
vertical-align, text-transform, text-overflow |
❌ Not supported |
border |
|
background-color, background-image |
✅ Supported |
background-size, background-position |
✅ Supported |
background-repeat, background-clip, background-origin |
✅ Supported |
background-attachment |
❌ Not supported |
box-shadow |
✅ Supported |
filter |
❌ Not supported |
No browser, just basics!
Himg calls through to the amazing blitz library, which uses Stylo to parse the CSS, servo/html5ever to parse the HTML, fetches network resources, builds a scene graph and hands over to vello to render an image.
Interaction between Ruby & Rust is done with the help of magnus, rb_sys and lots of glue code from the oxidize-rb team.
To play nicely with Rails a template handler is registered, which Rails' default_render method automatically calls when the corresponding view is found. This can be show.himg for a static image, or show.himg.erb to use variables from the controller. Additionally a Renderer is available with render himg: 'content' in case a view template is not needed.
- This is pre-alpha software, don't expect it to work perfectly yet.
- Performance needs tuning. Both in the underlying Blitz library and how data is passed between Rust and Ruby
- Network requests can be made: don't use this library with untrusted inputs. Use
disable_fetchif you don't need to fetch any resources. - file:// URLs are resolved: this could expose files on your computer. Use
disable_fetchif you don't need to fetch any resources.
- Run
bin/setupto install dependencies. - Run
rake specto run the tests with the default development setup - Run
appraisal rake specto run tests against different versions of rails and to confirm that the gem works in a plain ruby environment - Run
bin/consolefor an interactive prompt that will allow you to experiment. - Run
RAILS_ENV=development bundle exec spec/dummy/dummy_rails serverto check the dummy app.
- http://localhost:3000/users/jamedjo.png will display an opengraph compatible png
- http://localhost:3000/users/jamedjo.himg will also render the same png
- http://localhost:3000/users/jamedjo will render an HTML page with opengraph meta tags
- To install this gem onto your local machine, run
bundle exec rake install. - To simulate a headless server environment without a GPU, use
WGPU_BACKEND=empty bundle exec rspec - To profile performance with flamegraphs, run
bin/profile spec/fixtures/profile_test.html
bundle exec cargo run --example file
bundle exec cargo run --example file -- path/to/file.htmlTo release a new version:
- Run
rake bump:patchto update the version numbers inversion.rbandext/himg/Cargo.toml. - Run
bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the.gemfile to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/jamedjo/himg.
Copyright (c) 2025 James Edwards-Jones
This project is dual licenced under both the MIT and Apache 2.0 terms.
See: MIT License and APACHE 2.0 License
