Skip to main content
Now that we’ve created the Brand model, let’s create an Admin Dashboard interface so admins can manage brands — including editing the rich text description and uploading the logo.

Step 1: Scaffold the Admin UI

The Admin Scaffold generator creates a complete admin section for a resource:
spree generate spree:admin:scaffold Spree::Brand
This will create the following files:
File TypePathDescription
Controllerapp/controllers/spree/admin/brands_controller.rbHandles the logic for the brands resource.
Viewapp/views/spree/admin/brands/index.html.erbDisplays the list of brands using the tables system.
Viewapp/views/spree/admin/brands/new.html.erbDisplays the new brand form.
Viewapp/views/spree/admin/brands/edit.html.erbDisplays the edit brand form.
Partialapp/views/spree/admin/brands/_form.html.erbThe form partial used in new and edit views.
Initializerconfig/initializers/spree_admin_brands_table.rbRegisters the table with columns for the index view.
Initializerconfig/initializers/spree_admin_brands_navigation.rbAdds navigation to the admin sidebar.
It will also automatically add the following routes to your config/routes.rb file:
config/routes.rb
namespace :admin do
  resources :brands
end
You will now be able to access the brands resource at http://localhost:3000/admin/brands in your browser. To reference this route in your code, you can use the spree.admin_brands_path helper.

Step 2: Add the Description Editor and Logo Upload

The generated form only knows about the name column. Let’s add the rich text editor for description and the upload field for logo using Spree’s admin form builder:
app/views/spree/admin/brands/_form.html.erb
<div class="card mb-6">
  <div class="card-body">
    <%= f.spree_text_field :name, required: true, autofocus: true %>
    <%= f.spree_rich_text_area :description %>
  </div>
</div>

<div class="card mb-6">
  <div class="card-header">
    <h5 class="card-title"><%= Spree.t(:logo) %></h5>
  </div>
  <div class="card-body">
    <%= f.spree_file_field :logo, width: 300, height: 300 %>
  </div>
</div>
  • spree_rich_text_area renders a WYSIWYG editor (Trix) for the Action Text description.
  • spree_file_field handles drag-and-drop upload, image preview, and direct upload to storage. Add crop: true to enable image cropping with a recommended-size indicator.
Then permit the two new attributes in the controller:
app/controllers/spree/admin/brands_controller.rb
module Spree
  module Admin
    class BrandsController < ResourceController
      private

      def permitted_resource_params
        params.require(:brand).permit(
          :name,
          :description,
          :logo
        )
      end
    end
  end
end
Open http://localhost:3000/admin/brands/new, create a brand with a formatted description and a logo — everything saves through the standard form.

Step 3: Customize the Table Columns

The scaffold generator creates a table initializer at config/initializers/spree_admin_brands_table.rb with default columns:
config/initializers/spree_admin_brands_table.rb
Rails.application.config.after_initialize do
  Spree.admin.tables.register(:brands, model_class: Spree::Brand, search_param: :name_cont)

  Spree.admin.tables.brands.add :name,
    label: :name,
    type: :link,
    sortable: true,
    filterable: true,
    default: true,
    position: 10

  Spree.admin.tables.brands.add :created_at,
    label: :created_at,
    type: :datetime,
    sortable: true,
    filterable: true,
    default: true,
    position: 20

  Spree.admin.tables.brands.add :updated_at,
    label: :updated_at,
    type: :datetime,
    sortable: true,
    filterable: true,
    default: false,
    position: 30
end

Show the logo next to the name

To render the logo thumbnail in the name column — the same pattern the Products table uses — switch the column to a custom partial:
config/initializers/spree_admin_brands_table.rb
Spree.admin.tables.brands.add :name,
  label: :name,
  type: :custom,
  partial: 'spree/admin/tables/columns/brand_name',
  sortable: true,
  filterable: true,
  default: true,
  position: 10
And create the partial:
app/views/spree/admin/tables/columns/_brand_name.html.erb
<%# locals: (record:, column:, value:) %>
<%= link_to spree.edit_admin_brand_path(record), class: 'flex items-center gap-3 no-underline', data: { turbo_frame: '_top' } do %>
  <% if record.logo.attached? %>
    <%= spree_image_tag record.logo, width: 48, height: 48, class: 'rounded' %>
  <% end %>
  <span class="text-gray-900 font-medium"><%= record.name %></span>
<% end %>
For complete table customization options including column types, filtering, sorting, and bulk actions, see the Admin Tables guide.

Step 4: Customize Navigation

The scaffold generator also creates a navigation initializer at config/initializers/spree_admin_brands_navigation.rb:
config/initializers/spree_admin_brands_navigation.rb
Rails.application.config.after_initialize do
  Spree.admin.navigation.sidebar.add :brands,
    label: :brands,
    url: :admin_brands_path,
    icon: 'list',
    position: 55,
    active: -> { controller_name == 'brands' },
    if: -> { can?(:manage, Spree::Brand) }
end
You can customize the icon and position to fit your needs. For example, to use the “award” icon and place it between Products (30) and Customers (40):
Spree.admin.navigation.sidebar.add :brands,
  label: :brands,
  url: :admin_brands_path,
  icon: 'award',
  position: 35,
  active: -> { controller_name == 'brands' },
  if: -> { can?(:manage, Spree::Brand) }

Adding to an Existing Submenu

To add “Brands” to the Products submenu instead, use the parent option:
config/initializers/spree_admin_brands_navigation.rb
Rails.application.config.after_initialize do
  Spree.admin.navigation.sidebar.add :brands,
    label: :brands,
    url: :admin_brands_path,
    position: 50,
    parent: :products,
    active: -> { controller_name == 'brands' },
    if: -> { can?(:manage, Spree::Brand) }
end
After restarting your server, you’ll see the new “Brands” navigation link in the admin sidebar!
For complete navigation API documentation including all available options, submenu creation, badges, and more, see the Admin Navigation guide. The form helpers used in Step 2 are documented in Form Builder and Components.

Next Step

Brands are manageable in the admin. Now let’s connect them to Products: Extending Core Models.