Skip to content

Upgrade guide

Stay up to date

The up-to-date status of all the gems is available at github.com/avo-hq/avo/issues/4349

The upgrade process from Avo 3 to Avo 4 contains several important improvements and changes.

We've made these changes to improve consistency and usability of the API and we've added some new features. Here's what you need to know to upgrade your Avo 3 application to Avo 4.

Take these steps one by one in order to upgrade your app. You can follow it yourself or let your LLM do the heavy lifting:

Use this guide to upgrade this Avo 3 app to Avo 4.
Take it step by step and produce a markdown doc (avo-3-to-4-upgrade.md) with each chapter as an item, checking them off as you go.
https://docs.avohq.io/4.0/avo-3-avo-4-upgrade.html

Depending on how you use Avo you might not need to do all the steps.

Get started with Avo 4

Assuming you are upgrading your Avo 3 app, you need to do three things:

  1. Enroll to the Avo 4 beta program by going to avohq.io/try-4.

  2. Upgrade the Avo gems

This means updating your Gemfile to target the beta version of Avo and running the bundle update on the gems you are using avo, avo-pro, avo-advanced, and all other avo gems you are using to use a version greater than or equal to 4.0.0.beta. See what other gems you might have such as avo-nested, avo-rhino_field, etc. because they need to be updated too.

ruby
# in your Gemfile

# before
gem "avo"
gem "avo-advanced", source: "https://packager.dev/avo-hq/"

# after
gem "avo", ">= 4.0.0.beta"

source "https://packager.dev/avo-hq/" do
  # all or some of these
  gem "avo-pro", ">= 4.0.0.beta"
  gem "avo-advanced", ">= 4.0.0.beta"
  gem "avo-http_resource", ">= 4.0.0.beta"
  gem "avo-dynamic_filters", ">= 4.0.0.beta"
  gem "avo-pro", ">= 4.0.0.beta"
  gem "avo-menu", ">= 4.0.0.beta"
  gem "avo-nested", ">= 4.0.0.beta"
  gem "avo-dashboards", ">= 4.0.0.beta"
  gem "avo-collaboration", ">= 4.0.0.beta"
  gem "avo-forms", ">= 4.0.0.beta"
  gem "avo-kanban", ">= 4.0.0.beta"
  gem "avo-api", ">= 4.0.0.beta"
  gem "avo-http_resource", ">= 4.0.0.beta"
  gem "avo-reactive_fields", ">= 4.0.0.beta"
end

gem "avo-rhino_field", ">= 4.0.0.beta"
bash
# some or all of these
bundle update avo avo-advanced avo-nested avo-http_resource avo-dynamic_filters avo-pro avo-menu avo-dashboards avo-collaboration avo-forms avo-kanban avo-api avo-http_resource avo-reactive_fields avo-rhino_field
  1. Go through this guide to upgrade your app to Avo 4.

Icons

We started using the Tabler icons instead of the Heroicons. They are provided by the avo-icons gem and you can quickly search for them using the tabler icon search.

Try to use the Tabler icons instead of the Heroicons moving forward.

If you used any of our icons (eg: avo/resources), you should update them to use the new Tabler icons. Check this PR with changes in the icons: https://github.com/avo-hq/avo/pull/4342/changes

If you see some areas which look "exploded" in the app, it's because some icons are missing and you should update them.

Avatars and initials

Avo now uses the avatar and initials of a record or resource throughout the app.

You set the avatar using the avatar configuration (ex-profile photo). The avatar will be used by Avo in multiple places in the app like the Show and Edit views, and the new breadcrumbs. In addition you can use the avatar field to display the avatar in the Index view.

Avo 4 introduces significant improvements to the search functionality, with enhanced resource search and global search capabilities.

Resource search functionality has been significantly enhanced in Avo 4. The most notable improvement is that resource search now updates the current view (table, grid, map, or any other view type) to show only the relevant search results, providing a more intuitive and seamless search experience.

Previously, search results were displayed separately from the main view. Now, when you perform a search on a resource index page, the current view dynamically updates to display only the records that match your search criteria, maintaining the same view format you were using (whether table, grid, map, etc.).

Global search has undergone a major architectural change in Avo 4. The previous implementation using a customized Algolia autocomplete plugin has been completely replaced with a new, fully owned component powered by Hotwire.

This transition brings several significant benefits:

Fully owned component

  • Complete control: Avo now has full development control over the search experience
  • No external dependencies: No longer relies on Algolia's autocomplete plugin
  • Future enhancements: Much greater possibility for future improvements and customizations
  • Consistent experience: Better integration with Avo's overall design system

Enhanced navigation and keyboard shortcuts

The new search component includes improved keyboard navigation:

  • Ctrl + K or Cmd + K - Open global search
  • Up and Down arrow keys - Navigate through search results
  • Enter - Visit the selected record
  • Esc - Close the search modal

New "Show all results" functionality

The global search now includes a comprehensive results page:

  • Quick results: The search dropdown shows a limited number of results (respecting the configured limit)
  • Show all results page: A dedicated page that displays all matching results without the limit restriction
  • Seamless transition: Easy access from the search dropdown to view comprehensive results

Breaking changes and migration notes


Avo Pro mount point removal

To provide a cleaner public URL for the search page, Avo::Pro is no longer mounted under the avo-pro path prefix.

  • Previous mount point: .../avo-pro/...
  • New mount point: no prefix (mounted at the Avo engine root)

Most Avo::Pro generated links were for internal requests (such as reordering) and were not user-visible. With the introduction of the dedicated search page, the public path became visible, so we removed the avo-pro prefix to be able to use /admin/search?q=da as the public search page instead of /admin/avo-pro/search?q=da.

If you have hardcoded links that include the avo-pro prefix, update them to the new path or, preferably, use Rails route helpers going forward.

This is not breaking unless you used hardcoded URLs, if you used Rails path helpers, no action is needed.

Removed disabled_features configuration

The disabled_features configuration has been removed. It was previously used only for toggling the global search. Replace any usage with the new global_search configuration.

ruby
# Before
Avo.configure do |config|
  config.disabled_features = [:global_search]
end

# After
Avo.configure do |config|
  config.global_search = {
    enabled: false,
  }
end

Check the global search configuration for more information.

Removed help option

The help option in the search configuration is now obsolete and has been removed. If you were using this option in your search configuration, you should remove it:

ruby
class Avo::Resources::User < Avo::BaseResource
  self.search = {
    query: -> { query.ransack(name_cont: q, email_cont: q, m: "or").result(distinct: false) },
    help: -> { "Search by name or email address" } 
  }
end
Repositioned result_path option

The result_path option has been moved to the item configuration and renamed to path.

ruby
class Avo::Resources::User < Avo::BaseResource
  self.search = {
    query: -> { query.ransack(name_cont: q, email_cont: q, m: "or").result(distinct: false) },
    item: -> do
      {
        title: record.name,
        description: record.email,
        image_url: record.avatar.attached? ? main_app.url_for(record.avatar) : nil,
        image_format: :rounded,
        path: avo.resources_user_path(record) 
      }
    end,
    result_path: -> { avo.resources_user_path(record) } 
  }
end

Actions

Confirmation option renamed

  • What changed: The no_confirmation action option was renamed to confirmation and the default behavior flipped.
    • Avo 3: no_confirmation (default: false), set to true to skip the confirmation modal.
    • Avo 4: confirmation (default: true), set to false to skip the confirmation modal.

Static configuration

ruby
# Avo 3
class Avo::Actions::ExportCsv < Avo::BaseAction
  self.no_confirmation = true
end

# Avo 4
class Avo::Actions::ExportCsv < Avo::BaseAction
  self.confirmation = false
end

Dynamic configuration (lambda)

ruby
# Avo 3
self.no_confirmation = -> { arguments[:no_confirmation] || false }

# Avo 4
self.confirmation = -> { arguments.key?(:confirmation) ? arguments[:confirmation] : true }

If you customized the actions modal/view

  • Data attribute: data-action-no-confirmation-valuedata-action-confirmation-value
  • Stimulus value: noConfirmationconfirmation
  • Behavior: show the modal when confirmation is true, submit immediately when confirmation is false.

Layout

main_panel is obsolete

The main_panel DSL has been removed in Avo 4. Previously, main_panel was responsible for holding the header component (title, description, controls, etc.) along with your fields.

Te migration depends on your current setup:

main_panel is the first panel and has no sidebar

Replace main_panel directly with card:

ruby
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
  def fields
    main_panel do
    card do
      field :id, as: :id
      field :name, as: :text
    end
  end
end

main_panel is the first panel and has a sidebar

Replace main_panel with panel and wrap the fields (outside the sidebar) with card:

ruby
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
  def fields
    main_panel do
    panel do
      card do
        field :id, as: :id
        field :id, as: :id
      end

      sidebar do
        field :created_at, as: :date_time
      end
    end
  end

Content above main_panel

If you have content above main_panel, add an explicit header before the renamed main_panel:

ruby
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
  def fields
    panel do
      field :status, as: :badge
    end

    header 

    main_panel do
    card do
      field :id, as: :id
      field :name, as: :text
    end
  end
end

Renamed Avo::PanelComponent to Avo::UI::PanelComponent

If you used the panel component in custom HTML partials you should update the import to use the new name. The Avo::PanelComponent has been renamed to Avo::UI::PanelComponent.

erb
<%= render Avo::PanelComponent.new(title: "User information") do |c| %> 
<%= render Avo::UI::PanelComponent.new(title: "User information") do |c| %> 
  <% c.with_body do %>
    <%= render Avo::Fields::IdField.new(record: record) %>
    <%= render Avo::Fields::TextField.new(record: record, field: :name) %>
  <% end %>
<% end %>

Renamed with_tools slot to with_controls

The with_tools slot has been renamed to with_controls.

erb
<%= render Avo::UI::PanelComponent.new(title: "User information") do |c| %>
  <% c.with_tools do %> 
  <% c.with_controls do %> 
    <%= render Avo::Fields::IdField.new(record: record) %>
    <%= render Avo::Fields::TextField.new(record: record, field: :name) %>
  <% end %>
<% end %>

panel title in keyword arguments

The panel title is now given as a keyword argument to the panel method.

ruby
# before
panel "User information" do
  field :id, as: :id
  field :name, as: :text
end

# after
panel title: "User information" do
  field :id, as: :id
  field :name, as: :text
end

tab title in keyword arguments

The tab now takes a title keyword argument instead of the first positional argument.

ruby
tab "User information" do
tab title: "User information" do
  panel do
    field :id, as: :id
    field :name, as: :text
  end
end

Components

Renamed view type components

Several view type components have been renamed and moved from the Avo::Index namespace to Avo::ViewTypes:

Avo 3Avo 4
Avo::Index::ResourceMapComponentAvo::ViewTypes::MapComponent
Avo::Index::ResourceTableComponentAvo::ViewTypes::TableComponent

If you're using self.components in your resources to customize these components, update the keys accordingly:

ruby
# app/avo/resources/user.rb
class Avo::Resources::User < Avo::BaseResource
  self.components = {
    "Avo::Index::ResourceMapComponent": "Avo::Custom::ResourceMapComponent", 
    "Avo::Index::ResourceTableComponent": "Avo::Custom::ResourceTableComponent", 
    "Avo::ViewTypes::MapComponent": "Avo::Custom::ResourceMapComponent", 
    "Avo::ViewTypes::TableComponent": "Avo::Custom::ResourceTableComponent", 
  }
end

Nested association forms

If you use the nested option on has_many, has_one, or has_and_belongs_to_many fields (nested association forms on New and Edit), you must add the avo-nested gem in Avo 4. The feature is no longer bundled in the avo-advanced gem.

Add the gem to your Gemfile with the same https://packager.dev/avo-hq/ source and authentication you use for other private Avo gems, then run bundle install:

ruby
gem "avo-nested", source: "https://packager.dev/avo-hq/"

See Gem server authentication if you need to configure BUNDLE_PACKAGER__DEV. Full usage of the nested option is documented under Nested in Forms on the association field pages.

Pagination

Replace size with slots

If you configured any resource pagination using the size option, update your pagination option from size to slots.

ruby
self.pagination = -> do
  {
    size: ... 
    slots: ... 
  }
end

Check the slots documentation for more details.

The Breadcrumbs API has been improved. This is mostly an internal change but you might have a few add_breadcrumb calls in your code. Sarch for those and update them to the new API using positional arguments.

add_breadcrumb now takes keyword arguments instead of positional arguments.

ruby
add_breadcrumb "Home", root_path
ruby
add_breadcrumb title: "Home", path: root_path

add_breadcrumb now takes icon and initials options for a more immersive experience.

ruby
add_breadcrumb title: "Home", icon: "heroicons/outline/home", initials: "AM"
add_breadcrumb title: "Home", icon: "tabler/outline/home", initials: "PB"

Renamed profile_photo to avatar

The profile_photo configuration has been renamed to avatar.

ruby
# Before
self.profile_photo = {
  source: :profile_photo # an Active Storage field or a path
}

# After
self.avatar = {
  source: :avatar # an Active Storage field or a path
}

The new avatar field will be used throughtout the app to display the record in a visual way.

Renamed cover_photo to cover

The cover_photo configuration has been renamed to cover.

ruby
# Before
self.cover_photo = {
  source: :cover_photo # an Active Storage field or a path
}

# After
self.cover = {
  source: :cover # an Active Storage field or a path
}

Grid Item Badge DSL tweaks

The grid item badge configuration has been updated from flat properties to a nested hash structure.

ruby
# Avo 3.15
self.grid_view = {
  card: -> do
    {
      title: record.title,
      badge_label: record.status,        
      badge_color: status_color,         
      badge_title: "Status: #{record.status}"
    }
  end
}

# Avo 4
self.grid_view = {
  card: -> do
    {
      title: record.title,
      badge: {                           
        label: record.status,            
        color: status_color,             
        style: "solid",                  
        title: "Status tooltip",         
        icon: "heroicons/outline/check"
      }                                   
    }
  end
}

Migration steps

  1. Replace flat badge properties with a badge hash:

    • badge_labelbadge: { label: ... }
    • badge_colorbadge: { color: ... }
    • badge_titlebadge: { title: ... }
  2. Add optional new properties if needed:

    • badge: { style: ... } - Controls badge appearance (subtle or solid)
    • badge: { icon: ... } - Adds an icon to the badge

For detailed information about available colors, styles, and icons, see the Badge field documentation.

See the Grid Item Badge documentation for more details on all available options.

Discreet information updates

We've made a few updates to the discreet information API to make it more versatile.

API tweaks

  1. Removed id_text and id_badge as they didn't really look good. Use id instead.
  2. The timestamps_badge was removed
  3. New :created_at and :updated_at types which show the timestamps as a key-value pair.
  4. label is now text
  5. Renamed url_target to target
  6. as can be icon, text, badge, key_value
  7. key_value has key and value options

Removed cluser (or its alias row) in favor of width

  • removed compact and short options from the field wrapper. The fields react better
  • avo configuration has new use_stacked_fields option to enable the global use of the stacked field option
  • fields now have the width option

Map view tweaks

  • layout changed to map.position and now reflects the actual map position on the page (:top, :right, :bottom, :left)

Added label_help option

This option allows you to add a help text to the label of a field on Show and Edit views.

Related resources:

Cards description option renamed to discreet_description

The old description option was renamed to discreet_description The current description option is used for the card description under the title.

Removed config.full_width_index_view configuration for config.container_width

The config.full_width_index_view configuration has been removed in favor of the config.container_width configuration.

ruby
# Before
config.full_width_index_view = true

# After
config.container_width = { index: :full }

More info on the Container width section.

Added sidebar_toggle_visible configuration option

More info on the Toggle the sidebar button visibility section.

Added self.description option to actions

More info on the Description option section.

Added self.icon option to resources

More info on the Icon option section.

Added view.single? method

The view.single? method has been added to the view object.

ruby
if view.single?
  # Code for the "show", "edit", and "new" views
end

More info on the View object section.