Saying stuff about stuff.

Using Phlex in Sinatra with phlex-sinatra

Phlex already works with Sinatra (and everything else) but its normal usage leaves you without access to Sinatra’s standard helper methods. That’s why I created phlex-sinatra which lets you use Sinatra’s url() helper from within Phlex (along with the rest of the usual helper methods available in a Sinatra action).

To enable the integration use the phlex method in your Sinatra action and pass an instance of the Phlex view (instead of using .call to get its output):

get '/foo' do

You can now use Sinatra’s url() helper method directly and its other methods (params, request, etc) via the helpers proxy:

class MyView < Phlex::HTML
  def template
    h1 { 'Phlex / Sinatra integration' }
    p {
      a(href: url('/foo', false)) { 'link to foo' }
    pre { helpers.params.inspect }


It might not seem obvious at first why you’d use url() at all given that you mostly just pass the string you want to output 🤷🏻‍♂️ but I hit the issue immediately when I switched to Phlex in my Wordle results Sinatra/Parklife microsite hosted on GitHub Pages.

One of the main features of using Parklife is that your development flow remains completely unchanged. In development you start the server as usual which means the app is almost certainly served from the root /, but if the static site is hosted as a GitHub Pages repository site it’ll be served from /my-repository-name – which means all your links will be broken in production! It’s incredibly frustrating but luckily easily fixed.

Step 1 is to use Sinatra’s url() helper method wherever you need a URL (the false second argument means the scheme/host isn’t included):

link(href: url('/app.css', false), rel: 'stylesheet', type: 'text/css')

Step 2, configure a Parklife base:

Parklife.application.config.base = '/wordle'

It’s also possible to pass --base at build-time, in fact if you used Parklife to generate a GitHub Actions workflow (parklife init --github-pages) then it’s already configured to fetch your GitHub Pages site URL – whether it’s a custom domain or a standard repository site – and pass it to the build script so you won’t need to manually configure it as above.

Step 3 (profit?). The result is that when Parklife generates the static build Sinatra will know to serve the site from the /wordle subpath and will include the prefix on all url()-generated URLs:

<link href="/wordle/app.css" rel="stylesheet" type="text/css">

Another main reason to use the url() helper is to generate a full URL – for instance from within a feed or for an og:image social media preview link. In this case don’t pass the false second argument (it defaults to true) and the full URL will be generated. Once again you’ll need to configure Parklife with the correct base but once again it’s already taken care of if you generated the GitHub Actions workflow with parklife init --github-pages.