Unpoly Rails
The unpoly-rails gem integrates Unpoly's server protocol into
Rails, exposing helper methods in controllers, views, and helpers.
Gem: unpoly-rails — tracks Unpoly 3.x
Installation
# Gemfile
gem 'unpoly-rails'
Frontend assets via Asset Pipeline
// application.js
//= require unpoly
/* application.css
*= require unpoly
*/
Frontend assets via npm (esbuild, Webpacker, etc.)
import 'unpoly/unpoly.js'
import 'unpoly/unpoly.css'
Quick reference
| Helper | Purpose |
|---|---|
up? | Is this an Unpoly fragment request? |
up.target | CSS selector being updated (success) |
up.target = 'body' | Override the render target |
up.target?('.sel') | Is this selector targeted? |
up.fail_target | CSS selector targeted for failed responses |
up.fail_target?('.sel') | Is this selector targeted on failure? |
up.any_target?('.sel') | Is selector targeted for success or failure? |
up.validate? | Is this a form validation request? |
up.validate_names | Array of field names that triggered validation |
up.validate_name | First validating field name (or nil) |
up.validate_name?('email') | Is this specific field being validated? |
up.layer.mode | Layer mode: "root", "modal", "drawer", etc. |
up.layer.overlay? | Is the targeted layer an overlay? |
up.layer.root? | Is the targeted layer the root layer? |
up.layer.accept(value) | Accept the current overlay (raises CannotClose on root layer) |
up.layer.dismiss(value) | Dismiss the current overlay (raises CannotClose on root layer) |
up.layer.open(...) | Request frontend to open a new overlay |
up.layer.emit(type, props) | Emit event on the targeted layer |
up.layer.context | Layer context hash |
up.context | Alias for up.layer.context |
up.fail_layer.mode | Mode of layer targeted for failed responses |
up.origin_layer.mode | Mode of the layer that caused the request |
up.mode | Shortcut for up.layer.mode |
up.fail_mode | Shortcut for up.fail_layer.mode |
up.origin_mode | Shortcut for up.origin_layer.mode |
up.emit(type, props) | Emit event on document after update |
up.cache.expire | Expire the client-side cache (triggers revalidation) |
up.cache.expire('/path/*') | Expire matching URL pattern |
up.cache.evict | Evict entries from the client-side cache |
up.title = 'Title' | Set document title from server |
up.version | Unpoly JS version string (from X-Up-Version header) |
up.safe_callback("js()") | CSP-safe inline callback with nonce |
head(:no_content) | Render empty 204 response (replaces deprecated up.render_nothing) |
up.no_vary { } | Read Unpoly headers without setting Vary |
Reference files
Load when the user's question covers that topic:
- server-helpers.md — Full API reference: request inspection, response control, layer API, validation, events, cache control, context, CSP callbacks, failed forms, Vary headers, conditional GETs, common Rails patterns
- rails-integration.md — View helper syntax (
link_to,form_with,button_to, field helpers), Turbo coexistence (disabling Turbo Drive in Rails 7+), CSP setup withcsp_meta_tag, global follow-all config - patterns.md — End-to-end Rails patterns: drawer overlay helper, event-driven subinteractions (
up-accept-event/up-dismiss-event), create related record inline + validate parent form, authorization overlay vs root layer - gotchas.md — Real-world pitfalls:
[up-accept-location]matching the opening URL,[up-hungry]flash wiped unexpectedly, system test submit race with[up-validate]