Field options
Every Avo field accepts a set of common options that control its label, visibility, formatting, and behavior. This page walks through what you can do with them; the Field options API lists every option's type, default, and accepted values.
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
def fields
field :name, as: :text, sortable: true, placeholder: "John Doe"
end
endWith no options, a field shows up on the Index, Show, New, and Edit views with a humanized version of its id as the label.
Besides the common options, some fields respond to field-specific options — like options on the select field — documented on each field's page.
Change the field label
Pass name to display a different label than the humanized field id.
field :is_available, as: :boolean, name: "Availability"
If you localize your app, translate the label through the i18n conventions instead of hardcoding it.
Show and hide fields on different views
There will be cases where you want to show fields on some views and hide them on others. For example, you may want to display a field on the New and Edit views and hide it on the Index and Show views.
Use the visibility helpers hide_on, show_on, only_on, and except_on. They accept :index, :show, :new, :edit, and :preview, plus the shorthands :forms (:new and :edit), :display (:index and :show), and :all (only for hide_on and show_on).
field :body, as: :textarea, hide_on: [:index, :show]Be aware that a few fields override those options — for example, the id field hides itself on the Edit and New views.
Please read the detailed views page for more info.
Show fields conditionally
You might want to restrict some fields to be accessible only if a specific condition applies — for example, hide fields if the user is not an admin.
Use the visible option with a boolean or a block. Inside the block, you have access to the context object and the current resource. The resource has the current record object, too (resource.record).
field :is_featured, as: :boolean, visible: -> { context[:user].is_admin? } # show field based on the context object
field :is_featured, as: :boolean, visible: -> { resource.name.include? 'user' } # show field based on the resource name
field :is_featured, as: :boolean, visible: -> { resource.record.published_at.present? } # show field based on a record attributeWARNING
On form submissions, the visible block is evaluated in the create and update controller actions. That's why you have to check if the resource.record object is present before trying to use it.
# `resource.record` is nil when submitting the form on resource creation
field :name, as: :text, visible: -> { resource.record.enabled? }
# Do this instead
field :name, as: :text, visible: -> { resource.record&.enabled? }Compute the value with a block
You might need to show a field with a value you don't have in a database row. In that case, you may compute the value using a block that receives the record (the actual database record), the resource (the configured Avo resource), and the current view.
field 'Has posts', as: :boolean do
record.posts.present?
rescue
false
endFormat displayed values
Sometimes you will want to process the database value before showing it to the user. Inside every formatter block you have access to all the defaults that Avo::ExecutionContext provides plus value, record, resource, view, and field.
On every view
format_using formats the value on all views — including inside the inputs on forms, so return the raw value on form views if the user should edit it.
field :is_writer, as: :text, format_using: -> {
if view.form?
value
else
value.present? ? '👍' : '👎'
end
}
On specific views
If the formatting only applies to certain views, reach for the view-scoped variants — format_display_using, format_form_using, format_index_using, format_show_using, format_edit_using, or format_new_using — instead of branching on view yourself. When several are declared, the most specific one wins; see the precedence table.
field :is_writer, format_display_using: -> { value.present? ? '👍' : '👎' }
With Rails helpers
You can format using Rails helpers like number_to_currency (note that view_context is used to access the helper):
field :price, as: :number, format_using: -> { view_context.number_to_currency(value) }Parse the value before saving
When it's necessary to parse information before storing it in the database, the update_using option proves to be useful. Inside the block you can access the raw value from the form, and the returned value will be saved in the database.
field :metadata,
as: :code,
update_using: -> do
ActiveSupport::JSON.decode(value)
endMake columns sortable
Add sortable to any field to make that column sortable on the Index view.
field :name, as: :text, sortable: true
Related:
Sort computed fields and associations
When using computed fields or belongs_to associations, you can't set sortable: true because Avo doesn't know what to sort by. Pass a block instead — it receives the query and the direction and must return a query.
In the example of a Post that has_many Comments, you might want to order the posts by which one received a comment the latest:
class Avo::Resources::Post < Avo::BaseResource
field :last_commented_at,
as: :date,
sortable: -> {
query.includes(:comments).order("comments.created_at #{direction}")
}
endclass Post < ApplicationRecord
has_many :comments
def last_commented_at
comments.last&.created_at
end
endMark fields as required
To indicate that a field is mandatory, use the required option, which adds an asterisk to the field as a visual cue.
Avo automatically adds the asterisk when the model has a presence validator on the attribute, so you often don't need this option at all. It's purely cosmetic either way — add the actual validation to your model (validates :name, presence: true).
field :name, as: :text, required: true
# or conditionally
field :name, as: :text, required: -> { view == :new }
Prevent users from editing a field
Two options render the input as disabled on the New and Edit views — pick based on how much protection you need.
disabled also ignores the field's value on save. Even if a bad actor re-enables the input in the DOM and submits, the record is not updated.
field :name, as: :text, disabled: true
# or conditionally
field :id, as: :number, disabled: -> { view == :edit }
readonly only disables the input in the UI — a user can still re-enable it in the DOM and submit an arbitrary value. Use it for convenience, not protection.
field :name, as: :text, readonly: true
Set a default value
Use default to pre-fill the field on the New view (and in action modals) with a fixed value or a block.
# using a value
field :name, as: :text, default: 'John'
# using a callback function
field :level, as: :select, options: { 'Beginner': :beginner, 'Advanced': :advanced }, default: -> { Time.now.hour < 12 ? 'advanced' : 'beginner' }Add help text
Use help to display extra text — plain or HTML — below the input on the form views.
# using the text value
field :custom_css, as: :code, theme: 'dracula', language: 'css', help: "This enables you to edit the user's custom styles."
# using HTML value
field :password, as: :password, help: 'You may verify the password strength <a href="http://www.passwordmeter.com/">here</a>.'
If the text should appear on every view — not just forms — use label_help, which renders below the field's label.
field :custom_css, as: :code, theme: 'dracula', language: 'css', label_help: "This enables you to edit the user's custom styles."
Add a placeholder
Some fields support the placeholder option, which will be passed to the inputs on the New and Edit views when they are empty.
field :name, as: :text, placeholder: 'John Doe'
Place fields on the same row
The width option controls how much horizontal space a field takes inside its parent panel or card. Adjacent fields with a width below 100 (a percentage) sit side by side.
field :first_name, width: 50
field :last_name, width: 50
field :years_of_experience # full widthSetting any width below 100 automatically marks the field as stacked — the label moves above the value so the field fits the narrower column. See the supported values in the reference.
Stack the label above the value
For some fields, it might make more sense to use all of the horizontal area to display the value. Change the layout of the field wrapper using the stacked option.
field :meta, as: :key_value, stacked: trueinline layout (default)

stacked layout

Global stacked layout
You may also set all the fields to follow the stacked layout by changing the field_wrapper_layout initializer option from :inline (default) to :stacked.
# config/initializers/avo.rb
Avo.configure do |config|
config.field_wrapper_layout = :stacked
endNow, all fields will have the stacked layout throughout your app.
Avo 4 also adds use_stacked_fields, which stacks every field at the CSS level:
# config/initializers/avo.rb
Avo.configure do |config|
config.use_stacked_fields = true # default: false
endWith it enabled, fields render stacked by default without needing stacked: true on each one, and you can still override per field.
Store empty values as NULL
When a user saves a form, Avo stores the value for each field in the database as-is. If you prefer to store NULL when the field is empty, use the nullable option — it converts nil and empty values to NULL.
You may also define which values should be interpreted as NULL using null_values.
# using default null values (nil and "")
field :body, as: :textarea, nullable: true
# using custom null values
field :body, as: :textarea, nullable: true, null_values: ['0', '', 'null', 'nil', nil]Link the table cell to the record
Sometimes, on the Index view, you may want a field in the table to be a link to that resource so that you don't have to scroll to the right to click the Show icon. Use link_to_record to change a table cell into a link to that record. It's available on the id, text, gravatar, and belongs_to fields.
field :id, as: :id, link_to_record: true
field :name, as: :text, link_to_record: true
Optionally you can enable the global config id_links_to_resource, which links every id field automatically. More on that on the customization page.
Summarize a column
The summarizable option generates a visual summary of a column's data distribution. A chart icon appears in the table header; clicking it displays a summary chart based on the data in that column.
field :status, as: :select, summarizable: trueLet users copy the value
The copyable option shows a clipboard icon when hovering over the field's value, allowing easy copying. Particularly useful for unique identifiers, URLs, or other text users frequently need to copy.
field :name, as: :text, copyable: trueINFO
The copied value is the displayed value. If you truncate it with format_using, the truncated text is what gets copied — use CSS truncation via the html option if you need to display a short value but copy the full one.
Align text on the Index view
It's customary on tables to align numbers to the right. You can do that using the html option, which attaches classes, styles, and data attributes to the field's elements — see the HTML attributes page for everything it can do.
class Avo::Resources::Project < Avo::BaseResource
field :users_required, as: :number, html: {index: {wrapper: {classes: "text-right"}}}
end
Customize the field components
The components option lets you swap the view components used to render the field on the index, show, and edit views.
Eject the field components
To start customizing, eject one or multiple field components using the avo:eject command — it generates the files for all of the field type's components:
rails g avo:eject --field-components text --scope adminScope
If you don't pass a --scope when ejecting a field view component, the ejected component will override the default components all over the project.
Check the eject documentation for more details.
Point the field at your components
Pass a hash (or a block returning one) with <view>_component keys:
field :description,
as: :text,
components: {
index_component: Avo::Fields::Admin::TextField::IndexComponent,
show_component: Avo::Fields::Admin::TextField::ShowComponent,
edit_component: "Avo::Fields::Admin::TextField::EditComponent"
}field :description,
as: :text,
components: -> do
{
show_component: Avo::Fields::Admin::TextField::ShowComponent,
edit_component: "Avo::Fields::Admin::TextField::EditComponent"
}
endTarget a different database attribute
Use for_attribute to point a field at a different model attribute than its id — for example, to declare two fields backed by the same attribute with different presentations:
field :status, as: :select, options: [:one, :two, :three], only_on: :forms
field :secondary_field_for_status,
as: :badge,
for_attribute: :status,
options: {info: :one, success: :two, warning: :three},
except_on: :forms,
help: "Secondary field for status using the for_attribute option"Pass arbitrary data to the field
The meta option sends arbitrary information to the field — especially useful when you're building your own custom fields or using custom components for the built-in fields.
# meta as a hash
field :status,
as: :custom_status,
meta: {foo: :bar}
# meta as a block
field :status,
as: :badge,
meta: -> do
record.statuses.map(&:id)
endWithin your field template you can now access the @field.meta attribute:
<%= field_wrapper **field_wrapper_args do %>
<% if @field.meta[:foo] %>
<%= @resource.record.foo_value %>
<% else %>
<%= @field.value %>
<% end %>
<% end %>React to changes in other fields
The react_on option re-evaluates a field when other fields change in the form, refreshing @record with the latest form values. Updates run when the watched field's value is committed — on selection for selects and checkboxes, and when the input loses focus for text fields.
This feature is provided by the avo-reactive_fields add-on. Add the gem to your app before using react_on (see the Avo 4 upgrade guide for the packager.dev source).
Dependent select
In the example below, the city field reacts whenever the country select changes, so the available city options are always relevant to the selected country:
# app/avo/resources/course.rb
class Avo::Resources::Course < Avo::BaseResource
def fields
field :country,
as: :select,
options: Course.countries,
include_blank: "No country"
field :city,
as: :select,
react_on: :country,
options: -> { Course.cities.dig(@record.country&.to_sym) || [""] }
end
endDerived value (slug from name)
Pair react_on with format_using to re-compute a derived value whenever another field changes. When the user fills in name (for example Hello World) and the input loses focus, slug updates to hello_world — on each reactive request, @record is hydrated from the submitted form params, so format_using always sees the latest name, even before save:
# app/avo/resources/course.rb
class Avo::Resources::Course < Avo::BaseResource
def fields
field :name
field :slug,
react_on: :name,
format_using: -> { @record.name&.downcase&.gsub(" ", "_") }
end
endTIP
To retrieve the original value of a field before it was changed, use the *_was methods.