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.replace responses — 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_cms in your initializer)
  • inline_editing_check must be configured with a proc that returns true for 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

01

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.

ruby
# config/initializers/railspress.rb
Railspress.configure do |config|
  config.enable_cms
end
02

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.

javascript
import "railspress"
Note

If you already import "railspress" for other features (focal point, image cropping, etc.), you are all set — skip this step.

03

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.

ruby
# 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.

04

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:

erb
<!-- 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.

erb
<%# 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.

erb
<%# 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

  1. Admin hovers over CMS content — a subtle dashed blue outline appears
  2. Admin right-clicks — a floating editor panel opens at the cursor position
  3. Panel shows element metadata (group, name, version count), a textarea with current content, Save/Cancel buttons, and an "Open in admin" link
  4. Admin edits text and clicks Save
  5. Content updates on the page instantly (no reload), editor closes
  6. A version is automatically created in the admin panel

Known Limitations

Note

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 and style="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.