Custom controls
Avo displays a default set of buttons (controls) on the Index, Show, and Edit views and at the end of each table row. Custom controls let you take over any of those areas — relabel or remove the default buttons and add your own links, action buttons, and dropdowns.

Take over an area by assigning a block to the matching resource class attribute — show_controls, edit_controls, index_controls, or row_controls:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.show_controls = -> do
link_to "View on site", "https://fish.com", target: :_blank
default_controls
end
endAny area you don't customize keeps its default controls:

Inside a block you can mix the built-in controls (like back_button or actions_list) with your own link_to links and action buttons. See the API reference for every control and the options each one accepts.
Add controls to dashboard headers
Sinceavo-custom_controls >= v4.3.3With avo-dashboards installed, assign a dashboard's self.controls block to add native link_to, list, divider, partial, action, actions_list, and refresh_button controls. Use list label: "More" do, not a positional label. Declare controls rather than returning raw HTML.
Dashboard controls default to nil and require a license enabling avo-custom_controls. The block replaces the whole header, so declare refresh_button if you want the button. Default resource buttons are not available on dashboards. See dashboard header controls for a complete example, Rails forms, and authorization requirements.
Customize the show controls
On the Show view the default configuration is back_button, delete_button, detach_button, actions_list, and edit_button. Assign a show_controls block and declare the controls you want, in the order you want them:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.show_controls = -> do
back_button label: "", title: "Go back now"
link_to "Fish.com", "https://fish.com", icon: "heroicons/outline/academic-cap", target: :_blank
link_to "Turbo demo", "/admin/resources/fish/#{params[:id]}?change_to=new-content",
class: "custom-class",
data: {
turbo_frame: "fish_custom_action_demo"
}
delete_button label: "", title: "Delete this fish"
detach_button label: "", title: "Detach this fish"
actions_list label: "Runnables", exclude: [Avo::Actions::ReleaseFish], style: :primary, color: :slate
action Avo::Actions::ReleaseFish, style: :primary, color: :fuchsia, icon: "heroicons/outline/globe"
edit_button label: ""
end
end
Customize the edit controls
On the Edit and New views the default configuration is back_button, delete_button, actions_list, and save_button. Assign an edit_controls block — it applies to both views, so use view to differentiate when needed:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.edit_controls = -> do
back_button label: "", title: "Go back now"
link_to "Fish.com", "https://fish.com", icon: "heroicons/outline/academic-cap", target: :_blank
delete_button label: "", title: "Delete this fish"
detach_button label: "", title: "Detach this fish"
actions_list exclude: [Avo::Actions::ReleaseFish], style: :primary, color: :slate, label: "Runnables"
action Avo::Actions::ReleaseFish, style: :primary, color: :fuchsia, icon: "heroicons/outline/globe" if view != :new
save_button label: "Save Fish"
end
end
Customize the index controls
On the Index view the default configuration is attach_button, actions_list, and create_button. Assign an index_controls block:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.index_controls = -> do
link_to "Fish.com", "https://fish.com", icon: "heroicons/outline/academic-cap", target: :_blank
actions_list exclude: [Avo::Actions::DummyAction], style: :primary, color: :slate, label: "Runnables" if Fish.count > 0
action Avo::Actions::DummyAction, style: :primary, color: :fuchsia, icon: "heroicons/outline/globe" if Fish.count > 0
attach_button label: "Attach one Fish"
create_button label: "Create a new and fresh Fish"
end
end
Customize the row controls
At the end of each table row on the Index view the default configuration is order_controls, show_button, edit_button, detach_button, and delete_button. Assign a row_controls block:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.row_controls = -> do
action Avo::Actions::ReleaseFish, label: "Release #{record.name}", style: :primary, color: :blue,
icon: "heroicons/outline/hand-raised" unless params[:view_type] == "grid"
edit_button title: "Edit this Fish now!"
show_button title: "Show this Fish now!"
delete_button title: "Delete this Fish now!", confirmation_message: "Are you sure you want to delete this Fish?"
actions_list style: :primary, color: :slate, label: "Actions" unless params[:view_type] == "grid"
action Avo::Actions::ReleaseFish, title: "Release #{record.name}", icon: "heroicons/outline/hand-raised", style: :icon
link_to "Information about #{record.name}", "https://en.wikipedia.org/wiki/#{record.name}",
icon: "heroicons/outline/information-circle", target: :_blank, style: :icon
end
endThe same controls are displayed on the grid view items too — check params[:view_type] (as above) to show a control on only one of the two view types.

Keep the default controls
If you just want to add a link before or after the default controls, you don't have to re-declare them all — call default_controls where you want them to appear:
# app/avo/resources/post.rb
class Avo::Resources::Post < Avo::BaseResource
self.show_controls = -> do
# This link will be added before all other controls.
link_to "View on site", post_path(record), target: :_blank
default_controls
end
end
What you can put in a control
Beyond the built-in buttons, a control block accepts your own links and action buttons — and because a link_to forwards its data, class, and target to the rendered <a>, you can drive Turbo and Stimulus straight from a control.
Links
link_to takes a label and any path — an external URL, a Rails path helper, a mailto:, whatever you need:
self.show_controls = -> do
# External URL in a new tab
link_to "Docs", "https://avohq.io/docs", icon: "heroicons/outline/book-open", target: :_blank
# Rails path helper for the current record
link_to "View on site", post_path(record), icon: "heroicons/outline/globe-alt"
# mailto: link
link_to "Email author", "mailto:#{record.author.email}", icon: "heroicons/outline/envelope"
# Icon-only — the label becomes the tooltip
link_to "Open in Stripe", "https://dashboard.stripe.com/customers/#{record.stripe_id}",
icon: "heroicons/outline/credit-card", style: :icon
default_controls
endBuilt-in controls
Relabel, restyle, or reorder the built-in buttons — back_button, edit_button, show_button, delete_button, and the rest:
self.show_controls = -> do
back_button label: "", title: "Back" # icon-only
edit_button label: "Edit this fish"
delete_button label: "", icon: "heroicons/outline/trash",
confirmation_message: "Delete this fish for good?"
endActions
Pull an action out as its own button, pass it arguments, and keep the rest in the dropdown:
self.show_controls = -> do
# A single action as a button
action Avo::Actions::ReleaseFish, style: :primary, color: :fuchsia, icon: "heroicons/outline/globe"
# Send data to the action
action Avo::Actions::ExportSelection, arguments: { format: :csv }
# Everything else stays in the dropdown
actions_list exclude: [Avo::Actions::ReleaseFish], label: "More"
default_controls
endTurbo triggers
A link_to forwards its data hash to the <a>, so any data-turbo-* attribute works. Load a response into a Turbo Frame, or turn a link into a POST/DELETE button:
self.show_controls = -> do
# Render the response into a Turbo Frame already on the page
link_to "Preview", preview_fish_path(record),
icon: "heroicons/outline/eye",
data: { turbo_frame: "fish_preview" }
# A link that submits as a POST, with a confirmation dialog
link_to "Archive", archive_fish_path(record),
icon: "heroicons/outline/archive-box",
data: { turbo_method: :post, turbo_confirm: "Archive this fish?" }
default_controls
endJavaScript triggers
The same data pass-through lets you wire a control to a Stimulus controller — set the controller, action, and any value attributes, and Avo renders them on the link:
self.show_controls = -> do
link_to "Copy API key", "#",
icon: "heroicons/outline/clipboard",
data: {
controller: "clipboard",
action: "clipboard#copy",
clipboard_text_value: record.api_key
}
default_controls
endLoad the controller through your asset pipeline so Stimulus picks it up.
Group links and actions in a dropdown
If you have too many controls for the bar, group your custom links and actions in a list dropdown:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.index_controls = -> do
list label: "Custom Index List", icon: "heroicons/outline/cube-transparent", style: :primary, color: :slate, title: "A custom list" do
link_to "Google", "https://google.com", icon: "heroicons/outline/academic-cap"
action Avo::Actions::Sub::DummyAction, icon: "heroicons/outline/globe"
link_to "Fish.com", "https://fish.com", icon: "heroicons/outline/fire", target: :_blank
end
end
end
Within the list block you can use link_to, action, and divider — the built-in button controls are not allowed there.
Conditionally show controls
A control declared with action renders exactly where the Actions dropdown would offer that action. Avo checks the action's visible block and its authorize rule before drawing the button, so a hand-written guard around it usually just repeats what the action already says. Actions declared inside a list dropdown behave the same way.
When the condition is about whether the action applies to this record at all, put it on the action and every control that offers it follows:
# app/avo/actions/release_fish.rb
class Avo::Actions::ReleaseFish < Avo::BaseAction
self.visible = -> { record.released? }
endBecause the controls declaration is a block, you can still wrap any control in a regular if/else. Reach for that when you want this control hidden for a reason that isn't the action's own rule — most often where it's being drawn:
# app/avo/resources/fish.rb
class Avo::Resources::Fish < Avo::BaseResource
self.row_controls = -> do
# Placement, not availability: this same block also draws the grid cards, and
# the button is too wide for them. Moving this into the action's own `visible`
# block would pull it from the Actions dropdown everywhere too.
unless params[:view_type] == "grid"
action Avo::Actions::ReleaseFish, style: :primary, color: :fuchsia, icon: "heroicons/outline/globe"
end
show_button label: ""
end
endINFO
The built-in button controls (edit_button, delete_button, detach_button, …) have no visible block of their own. Avo still hides them when your policy says so — edit_button follows edit?, delete_button follows destroy? — but any other condition on those has to be a plain if.