A layout boundary for related direct or FormBuilder-backed fields.
app/components/nitro_kit/field_group.rbNitroKit::FieldGroup.new { fields } or form.group { fields }FieldGroup owns layout only; native field meaning remains with its composed children.
test/dummy/app/components/gallery/components/field_group_page.rbsample("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 }
endtest/dummy/app/components/gallery/components/field_group_page.rbrender 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."
)
endBuilder groups organize related model fields while Rails continues to own names, values, and errors.
test/dummy/app/components/gallery/components/field_group_page.rbprofile = 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")
endLong labels and guidance do not require a different grouping API.
test/dummy/app/components/gallery/components/field_group_page.rbrender 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