Field group

A layout boundary for related direct or FormBuilder-backed fields.

Source
app/components/nitro_kit/field_group.rb
API
NitroKit::FieldGroup.new { fields } or form.group { fields }

Grouping boundaries

FieldGroup owns layout only; native field meaning remains with its composed children.

Direct groups

One field
Empty
Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/field_group_page.rb
sample("One field", slug: "one") do
  render NitroKit::FieldGroup.new(html: { id: "gallery-field-group-one" }) do
    render NitroKit::Field.new(
      nil,
      :workspace_name,
      id: "gallery-field-group-workspace-name",
      name: "workspace[name]",
      value: "Mothership",
      label: "Workspace name",
      required: true
    )
  end
end
sample("Empty", slug: "empty") do
  render NitroKit::FieldGroup.new(html: { id: "gallery-field-group-empty" }) { nil }
end

Mixed controls

Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/field_group_page.rb
render NitroKit::FieldGroup.new(html: { id: "gallery-field-group-mixed" }) do
  render NitroKit::Field.new(
    nil,
    :summary,
    as: :textarea,
    id: "gallery-field-group-summary",
    name: "workspace[summary]",
    value: "Release planning and production operations.",
    label: "Summary"
  )
  render NitroKit::Field.new(
    nil,
    :time_zone,
    as: :select,
    id: "gallery-field-group-time-zone",
    name: "workspace[time_zone]",
    value: "Europe/Copenhagen",
    label: "Time zone",
    options: Gallery::Forms::Profile::TIME_ZONES
  )
  render NitroKit::Field.new(
    nil,
    :weekly_digest,
    as: :checkbox,
    id: "gallery-field-group-weekly-digest",
    name: "workspace[weekly_digest]",
    label: "Send a weekly digest",
    checked: true
  )
  render NitroKit::Field.new(
    nil,
    :deployment_alerts,
    as: :switch,
    id: "gallery-field-group-deployment-alerts",
    name: "workspace[deployment_alerts]",
    label: "Deployment alerts",
    description: "Notify workspace owners after production deploys."
  )
end

Rails form builder

Builder groups organize related model fields while Rails continues to own names, values, and errors.

Profile sections

Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/field_group_page.rb
profile = Gallery::FormExamples.profile(:invalid)

form_with(
  model: profile,
  scope: :profile,
  url: "#profile-groups",
  builder: NitroKit::FormBuilder,
  id: "gallery-field-group-profile-form"
) do |form|
  form.group(html: { id: "gallery-field-group-profile-identity" }) do
    form.field(:name, id: "gallery-field-group-profile-name", required: true)
    form.field(
      :email,
      as: :email,
      id: "gallery-field-group-profile-email",
      description: "Used for account recovery.",
      required: true
    )
  end
  form.group(html: { id: "gallery-field-group-profile-details" }) do
    form.field(
      :bio,
      as: :textarea,
      id: "gallery-field-group-profile-bio",
      description: "Shown to workspace members."
    )
    form.field(
      :time_zone,
      as: :select,
      id: "gallery-field-group-profile-time-zone",
      prompt: "Choose a time zone",
      options: Gallery::Forms::Profile::TIME_ZONES,
      required: true
    )
  end
  form.submit("Save profile", id: "gallery-field-group-profile-save")
end

Long grouped form

Long labels and guidance do not require a different grouping API.

Credential policy

Viewport640 px · sm
Rubytest/dummy/app/components/gallery/components/field_group_page.rb
render NitroKit::FieldGroup.new(html: { id: "gallery-field-group-pressure" }) do
  render NitroKit::Field.new(
    nil,
    :rotation_window,
    as: :number,
    id: "gallery-field-group-rotation-window",
    name: "credential_policy[rotation_window]",
    value: 14,
    label: "Number of days before expiration when replacement credentials should be generated",
    description: "A notification is sent first; production credentials are never revoked until every target confirms receipt.",
    min: 1,
    max: 90,
    required: true
  )
  render NitroKit::Field.new(
    nil,
    :confirmation,
    as: :checkbox,
    id: "gallery-field-group-rotation-confirmation",
    name: "credential_policy[confirmation]",
    label: "Require owner confirmation before revoking the previous production credential",
    checked: true
  )
end