RRUI-001 · Form fields · Validation

Input & Textarea ignore ActiveRecord errors on 422

After a failed save, f.rui_input / f.rui_textarea used to re-render with no field error text and no invalid styling — even though the model had errors. Select already did this correctly.

Fixed · 0.51.2 reported against 0.51.1 resolved 2026-08-07

Resolution (gem owner). Input and Textarea were built before server-side error rendering was standardised across form components, and they never got retrofitted while the docs said otherwise. As of v0.51.2 they render the field error with the proper red state and aria wiring — same as Select. The reports also prompted a broader form audit shipping as v0.53.0 (consistent error accessibility on every field, among other fixes).

Summary

Components RapidRailsUI::Input::Component, RapidRailsUI::Textarea::Component
Status Fixed in 0.51.2
Cause Pre-standard components never retrofitted to the shared server-error path
Now Field error message + invalid border + aria, matching Select

Repro (historical)

  1. Bind f.rui_input(:email, type: :email, required: true) to an ActiveRecord model.
  2. On create failure, re-render the form at status: :unprocessable_entity.
  3. Submit with a blank/invalid email (bypass HTML required if needed so the request reaches the server).
  4. Was: Form returns; email field has no message, no red border, no aria-invalid.
  5. Expected / now: Same as f.rui_select — first model error under the field, invalid border, aria-invalid="true".
Client Stimulus validation did not cover this historically: input_validation_controller / textarea_validation_controller skipped validation on connect. After a 422 replace, nothing painted the server error until SSR used field_has_error? / error_message.

Evidence (0.51.1)

FormHelper already had the API

def field_has_error?
  @form && @method && @form.object.errors[@method].present?
end

def error_message
  return nil unless field_has_error?
  @form.object.errors[@method].first
end

Input template never filled the message

<% if validation %>
  <p id="<%= validation_message_id %>"
     class="<%= VALIDATION_MESSAGE_CLASSES %>"
     role="alert"
     data-…-target="message"></p>
<% end %>

Always empty. Select already did:

<% if render_error_message? %>
  <p class="<%= ERROR_CLASSES %>" role="alert">
    <%= error_message %>
  </p>
<% end %>

Stimulus skipped paint on connect

connect() {
  // Don't validate on connect - wait for user interaction.
}

What landed in the gem