Skip to content
Looking for every option?See the full API reference →

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.

ruby
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
  def fields
    field :name, as: :text, sortable: true, placeholder: "John Doe"
  end
end

With 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.

ruby
field :is_available, as: :boolean, name: "Availability"
An Avo index table with three columns — ID, Name and a boolean column whose header reads “Availability”, the custom label set via the field's name option, highlighted.

If you localize your app, translate the label through the i18n conventions instead of hardcoding it. Fields pick up the model's attribute translations (activerecord.attributes.<model>.<attr>) on their own, so a translated model usually needs no name at all.

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).

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

You can declare the same field more than once, each for its own views. Saving the form goes through the declaration the form shows, so its attach_scope, update_using, and other save rules apply:

ruby
field :user, as: :belongs_to, only_on: :forms, attach_scope: -> { query.where(active: true) }
field :user, as: :belongs_to, only_on: :display

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).

ruby
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 attribute

WARNING

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.

ruby
# `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? }

Hide fields that have no value ​

Since avo >= v4.2.11

Some fields only matter when they're set. A customer might have a Stripe id or a Paddle id, never both. Pass hide_if_blank the views where a blank field should disappear, instead of writing a visible block for each one.

ruby
field :stripe_id, as: :text, visible: -> { resource.record&.stripe_id.present? } 
field :paddle_id, as: :text, visible: -> { resource.record&.paddle_id.present? } 
field :stripe_id, as: :text, hide_if_blank: :all
field :paddle_id, as: :text, hide_if_blank: :all

It takes :index, :show, :new, :edit, the shorthands :forms and :display, or :all, alone or in an array. Use :display to keep the field on the forms so users can still fill it in.

ruby
field :notes, as: :textarea, hide_if_blank: :display

A few things to know:

  • "Blank" is Rails' blank?: nil, "", [], {}, and false. A boolean set to false is hidden too.
  • On the Index view, the column shows as long as one row on the page has a value. Rows where it's blank show the usual —.
  • On the Edit view, a field that is blank in the database is hidden, so it can't be filled in from the form.
  • On the New view, a default counts as a value, so a field with a default still shows.
  • It works on top of visible, show_on, hide_on, and the other visibility options. A field has to pass all of them to show.

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.

ruby
field 'Has posts', as: :boolean do
  record.posts.present?
rescue
  false
end

INFO

Computed fields are displayed only on the Show and Index views.

Format 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.

ruby
field :is_writer, as: :text, format_using: -> {
  if view.form?
    value
  else
    value.present? ? '👍' : '👎'
  end
}
An Avo index table with three columns — ID, Name and an “Is writer” column whose cells show a 👍 or 👎 emoji rendered via format_using instead of the raw value.

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.

ruby
field :is_writer, format_display_using: -> { value.present? ? '👍' : '👎' }
An Avo Show view details panel card laid out in three rows — ID spanning the full width on top, First name and Last name side by side, then Is writer and User Email side by side — the “Is writer” value showing a 👍 emoji rendered via format_display_using.

With Rails helpers ​

You can call Rails helpers such as number_to_currency directly inside a formatter. Use a display formatter when the result is not a valid form input value:

ruby
field :price, as: :number, format_display_using: -> { 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.

ruby
field :metadata,
  as: :code,
  update_using: -> do
    ActiveSupport::JSON.decode(value)
  end

Make columns sortable ​

Add sortable to any field to make that column sortable on the Index view.

ruby
field :name, as: :text, sortable: true
An Avo index table for Projects sorted by the Name column, whose header shows the active sort-arrow indicator for the sortable name text field.

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:

ruby
class Avo::Resources::Post < Avo::BaseResource
  field :last_commented_at,
    as: :date,
    sortable: -> {
      query.includes(:comments).order("comments.created_at #{direction}")
    }
end
ruby
class Post < ApplicationRecord
  has_many :comments

  def last_commented_at
    comments.last&.created_at
  end
end

Mark 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).

ruby
field :name, as: :text, required: true

# or conditionally
field :name, as: :text, required: -> { view == :new }
An Avo Edit form text field whose label has a red asterisk marking it as required.
Watch the demo video

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.

ruby
field :name, as: :text, disabled: true

# or conditionally
field :id, as: :number, disabled: -> { view == :edit }
An Avo Edit form with a disabled, greyed-out 'Name' text field above a normal, editable 'Website' field — the contrast shows the disabled state.

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.

ruby
field :name, as: :text, readonly: true
An Avo Edit form with a readonly, greyed-out 'Name' text field above a normal, editable 'Website' field — the contrast shows the readonly state.

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.

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

ruby
# 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>.'
An Avo Edit form 'Custom CSS' text field with a line of help text shown directly below the input explaining what the field does.

If the text should appear on every view — not just forms — use label_help, which renders below the field's label.

ruby
field :custom_css, as: :code, theme: 'dracula', language: 'css', label_help: "This enables you to edit the user's custom styles."
An Avo Edit form 'Custom css' code field with a line of help text shown directly below the field label explaining what the field does.

Add a tooltip ​

Help text takes up space on every row. When the extra context only matters on demand — the exact time behind a "Revoked" badge, the source of a computed number — use tooltip instead. The text shows in a bubble when the user hovers the value: the badge or text on Index and Show, the input on New and Edit.

ruby
field :status,
  as: :badge,
  options: {success: :active, danger: :revoked},
  tooltip: -> { record.revoked_at? ? "Revoked at #{record.revoked_at}" : nil }
An Avo Show page 'Stage' badge field with the mouse over the green 'Done' badge and a tooltip bubble above it reading 'Apollo is done'.

Both options accept a string or a block. Inside the block you get the same context as format_using — record, resource, view, and field — and returning nil renders no tooltip. HTML is allowed, the same as help.

label_tooltip puts the bubble on the label instead, on every view and on the Index table header. An info icon next to the label marks it as hoverable.

ruby
field :mrr, as: :number, label_tooltip: "Monthly recurring revenue, net of refunds"
An Avo Show page 'Stage' field whose label carries a small info icon, with the mouse over the label and a tooltip bubble above it reading 'Where the project is in its lifecycle'.

INFO

On the Index header there is no record, so a label_tooltip block must not depend on record.

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.

ruby
field :name, as: :text, placeholder: 'John Doe'
An Avo New form text field whose empty input shows the grey placeholder text “John Doe”.

Focus a field when the form opens ​

Since avo >= v4.3.1

Set autofocus on the field users fill in first, and the cursor starts there when the New or Edit form opens. It works in action modals too, where it replaces the default of focusing the first input.

ruby
field :title, as: :text, autofocus: true

Pass a block to decide per form. This one focuses the field on the New view only, so opening an existing record for editing doesn't put the cursor in the title.

ruby
field :title, as: :text, autofocus: -> { view.new? }

Most inputs and selects support it, belongs_to included. Date pickers, tag inputs, and rich text editors don't. A field inside a closed tab, or one that is readonly or disabled, can't take focus. The API reference lists every supported field.

Some forms own focus themselves and ignore the option. A field of a nested association form leaves the cursor on the parent form, a reactive re-render doesn't pull the cursor back to the field it replaces, and an action card in an Avo AI chat keeps focus in the chat composer.

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.

ruby
field :first_name, width: 50
field :last_name,  width: 50
field :years_of_experience # full width

Setting any width below 100 automatically marks the field as stacked — the label moves above the value so the field fits the narrower column. Declare stacked: false on the field if you'd rather keep the label beside the value. 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.

ruby
field :meta, as: :key_value, stacked: true

The option works the other way around too. Avo stacks fields on its own in a few places — inside a sidebar, in the record preview, and whenever a field has a width below 100 — and stacked: false opts a field out of all of them.

ruby
field :name, as: :text, stacked: false

inline layout (default) ​

An Avo show view key_value 'Meta' field in the default inline layout, the field label beside a key/value control listing three pairs (environment: production, region: eu-west, tier: premium).

stacked layout ​

An Avo show view key_value 'Meta' field in the stacked layout, the field label shown above a key/value control listing three pairs (environment: production, region: eu-west, tier: premium).

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.

ruby
# config/initializers/avo.rb
Avo.configure do |config|
  config.field_wrapper_layout = :stacked
end

Now, 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:

ruby
# config/initializers/avo.rb
Avo.configure do |config|
  config.use_stacked_fields = true # default: false
end

With 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.

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

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, number, date, date_time, time, gravatar, file, external_image, and belongs_to fields.

ruby
field :id, as: :id, link_to_record: true
field :name, as: :text, link_to_record: true
An Avo index table where the Name column cells are rendered as blue links to each record via link_to_record.

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.

ruby
field :status, as: :select, summarizable: true

The chart reads the same query the list does, so it follows the policy Scope and asks index?, or the parent's view_{association}? for a column on an association panel.

Let 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.

ruby
field :name, as: :text, copyable: true

INFO

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.

ruby
class Avo::Resources::Project < Avo::BaseResource
  field :users_required, as: :number, html: {index: {wrapper: {classes: "text-right"}}}
end
An Avo index table where the Users required number column is right-aligned via the html option, its numbers hugging the right edge of the column, contrasting with the left-aligned text columns.

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:

bash
rails g avo:eject --field-components text --scope admin

Scope

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:

ruby
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"
  }
ruby
field :description,
  as: :text,
  components: -> do
    {
      show_component: Avo::Fields::Admin::TextField::ShowComponent,
      edit_component: "Avo::Fields::Admin::TextField::EditComponent"
    }
  end

Target 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:

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

ruby
# 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)
  end

Within your field template you can now access the @field.meta attribute:

erb
<%= 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:

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

Derived 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:

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

TIP

To retrieve the original value of a field before it was changed, use the *_was methods.