Inline Editing
Edit content directly on the frontend without switching to the admin interface.
Overview
Inline editing lets admins right-click any CMS-powered text on a live page and edit it in place. Changes save instantly via Turbo Streams, and auto-versioning captures every edit. No page reload, no context switch to the admin panel.
How It Works
The system is built on three layers:
- Stimulus controller (
rp--cms-inline-editor) handles right-click interception, menu positioning, lazy form loading, and close behavior - Turbo Frames provide two nested frames per element: a display frame showing the current content, and a form frame that lazy-loads the edit form on first right-click
- Dual Turbo Streams on save: the controller returns two
turbo_stream.replaceresponses — one to update the form (with fresh version count) and one to update the display content
The wrapper element is a <span style="display:contents">, which is valid inside <h1>, <p>, and block elements with zero layout impact.
Prerequisites
- CMS must be enabled (
config.enable_cmsin your initializer) inline_editing_checkmust be configured with a proc that returnstruefor admin requests- The host app must import RailsPress JavaScript (
import "railspress")
Key Design Decisions
| Decision | Choice | Why |
|---|---|---|
| Wrapper element | <span style="display:contents"> |
Zero layout impact, valid in inline and block contexts |
| CSS delivery | Injected <style> tag via content_for :head |
Zero host app configuration needed |
| Form loading | Lazy (on first right-click) | No network requests until admin actually wants to edit |
| Multi-editor | One at a time | Opening a new editor closes any existing one |
| Content update | Dual Turbo Streams | Replaces both form frame and display frame on save |
| Image elements | Excluded | Only text-type elements get inline editing |
Setup
Enable CMS
Inline editing requires CMS to be enabled. If you have not already, add config.enable_cms to your initializer. Without this, setting inline_editing_check will raise a Railspress::ConfigurationError.
# config/initializers/railspress.rb
Railspress.configure do |config|
config.enable_cms
end
Import RailsPress JavaScript
Add this line to your host app's app/javascript/application.js. This auto-registers all RailsPress Stimulus controllers, including the inline editor. The engine configures the importmap pins automatically.
import "railspress"
If you already import "railspress" for other features (focal point, image cropping, etc.), you are all set — skip this step.
Configure the Admin Check
Set a proc that determines whether the current request should see inline editing controls. The proc receives the view helper context, so you can check authentication, roles, feature flags, or anything available in your views.
# config/initializers/railspress.rb
Railspress.configure do |config|
config.enable_cms
config.inline_editing_check = ->(context) {
# `context` is the view helper context
# (has access to controller, session, etc.)
context.controller.current_user&.admin?
}
end
If the proc raises an error, inline editing silently disables itself. Set to nil (the default) to disable inline editing entirely.
Add yield :head to Your Layout
The inline editor CSS is injected as a <style> tag via content_for :head. Add this to your application layout if you don't have it already:
<!-- app/views/layouts/application.html.erb -->
<head>
<!-- ... other head content ... -->
<%= yield :head %>
<%= javascript_importmap_tags %>
</head>
Usage
cms_element (Inline Editing Enabled)
Use the block form of cms_element to get inline editing support. When inline editing is active for the current request, RailsPress wraps the block output in a Stimulus-controlled <span> with right-click handling, lazy-loaded form, and Turbo Frame targets.
<%# This gets inline editing when enabled: %>
<%= cms_element(group: "Headers", name: "Homepage H1") do |value| %>
<h1><%= value %></h1>
<% end %>
cms_value (Read-Only)
cms_value returns a raw string with no wrapper element. It does not support inline editing regardless of configuration.
<%# This does NOT get inline editing (raw string): %>
<h1><%= cms_value("Headers", "Homepage H1") %></h1>
Stimulus Controller Data Attributes
When inline editing is active, cms_element renders a wrapper with these Stimulus data attributes:
| Attribute | Purpose |
|---|---|
data-controller="rp--cms-inline-editor" |
Registers the Stimulus controller |
data-rp--cms-inline-editor-inline-path-value |
GET path for lazy form loading |
data-rp--cms-inline-editor-frame-id-value |
Display Turbo Frame ID |
data-rp--cms-inline-editor-form-frame-id-value |
Form Turbo Frame ID |
data-rp--cms-inline-editor-element-id-value |
Content element database ID |
data-action="contextmenu->rp--cms-inline-editor#open" |
Right-click opens the editor |
User Flow
- Admin hovers over CMS content — a subtle dashed blue outline appears
- Admin right-clicks — a floating editor panel opens at the cursor position
- Panel shows element metadata (group, name, version count), a textarea with current content, Save/Cancel buttons, and an "Open in admin" link
- Admin edits text and clicks Save
- Content updates on the page instantly (no reload), editor closes
- A version is automatically created in the admin panel
Known Limitations
These limitations apply to the current release:
- Mobile/touch: No right-click on touch devices. Long-press support is a future enhancement.
- Image elements: Only text-type elements are editable inline. Image editing requires the admin panel.
- Concurrent edits: Last-write-wins. Version history preserves the audit trail.
- CSP strict mode: The inline
<style>tag andstyle="display:contents"may need adjustment for strict Content Security Policy environments. - Multi-instance: When the same element appears multiple times on a page, only the edited instance updates. Other instances show the old value until page reload.