docs/developer/upgrades/5.2-to-5.3.mdx
bundle update
bin/rake spree:install:migrations && bin/rails db:migrate
This will setup new database schema for the new features in Spree 5.3 like Price Lists and Customer Groups.
Admin Panel was completely rewritten to use Tailwind v4 so you need to re-run the install generator to setup Tailwind and remove any Dart Sass related files.
bin/rails g spree:admin:install
This will also modify your Procfile.dev file to include the new Tailwind CSS watch task instead of the old Dart Sass watch task.
You also need to add listen gem to your Gemfile in the development group, unless you already have it.
group :development do
gem 'listen', '>= 3.0'
end
Spree 5.3 introduces counter caches for products, taxons and variants. You need to run the following rake tasks to populate the counter caches:
bin/rake spree:products:reset_counter_caches
bin/rake spree:taxons:reset_counter_caches
bin/rake spree:variants:reset_counter_caches
This will greatly improve the performance of your application by avoiding N+1 queries.
<Warning> You will need to run this rake task locally and on your production environment to ensure that the counter caches are populated correctly. </Warning>Spree 5.3 introduces product metrics for products. You need to run the following rake task to populate the product metrics:
bin/rake spree:products:populate_metrics
This will populate the product metrics for all products in your database. Product metrics are used to sort products by best selling scope.
<Warning> You will need to run this rake task locally and on your production environment to ensure that the product metrics are populated correctly. </Warning>This rake task will enqueue background jobs to populate the product metrics for all products in your database so you need to make sure that your background jobs are configured correctly.
auto_strip_attributes gem usageauto_strip_attributes gem was removed from Spree due to bugs and conflicts with translations feature. Also it's not required anymore as built-in Rails normalizes provides the same feature without an additional depepdency.
If you used auto_stripe_attributes in your application, you will need to change it to normalizez, eg.
Replace
auto_strip_attributes :name
With:
normalizes :name, with: ->(value) { value&.to_s&.squish&.presence }
Spree 5.3 requires Tailwind v4 as it's also used now for the Admin panel so you need to update your Storefront to Tailwind v4 as well.
If you didn't update your Storefront to Tailwind v4 in the previous upgrade guide, you need to do it now.
Running bundle update previously installed Tailwind v4. You only need to run
bin/rails g spree:storefront:install
This will:
config/tailwind.config.js file with the new Tailwind v4 configuration.app/assets/tailwind/application.css file with the new Tailwind v4 styles. If you modified app/assets/stylesheets/application.tailwind.css file, you will need to merge your changes with the new one.pagy instead of kaminari (Optional)Spree 5.3 introduces pagy as the default pagination gem. Pagy is a faster and more memory-efficient pagination gem than Kaminari. This works automatically for the API and Admin panel.
For Storefront, you need to either update your theme (if you're not using the default theme) or set use_kaminari_pagination preference to true in your config/initializers/spree.rb file.
Please replace the contents of your show_more_button.html.erb with the following code:
<% if storefront_pagy&.next %>
<%= turbo_frame_tag "next_page", src: url_for(params.to_unsafe_h.merge(page: storefront_pagy.next, format: :turbo_stream)), class: "block relative w-full", data: { controller: "infinite-scroll", infinite_scroll_offset_value: "1350px" }, loading: "lazy" do %>
<span class="flex justify-center gap-2 items-center py-4 left-0 w-full h-full">
<%= render 'spree/shared/icons/spinner' %>
<%= Spree.t(:loading) %>...
</span>
<% end %>
<% end %>
In the following files:
page_sections/_post_grid.html.erbposts/_pagination.html.erbReplace this line:
<%= paginate @posts, theme: 'storefront', outer_window: 1, inner_window: 2 %>
with this line:
<%= render 'spree/shared/pagination' %>
Page Builder was extracted to a separate gem (spree_page_builder) so you need to include the Page Builder factories in your test suite if you were using them in your tests
Add this line to your spec_helper.rb file:
require 'spree/page_builder/testing_support/factories'
If you maintain a Spree extension, the following changes may be required to ensure compatibility with Spree 5.3.
prepend_before_action for filters that modify params[:q]In Spree 5.3, the ResourceController#load_resource before_action calls collection which builds the search query using search_params. If your extension uses before_actions to load data needed for filtering (e.g., loading a vendor to filter products), you must use prepend_before_action to ensure your filter runs before load_resource.
Before (Spree 5.2):
module MyExtension
module Admin
module ProductsControllerDecorator
def self.prepended(base)
base.before_action :load_vendor, only: :index
end
end
end
end
After (Spree 5.3):
module MyExtension
module Admin
module ProductsControllerDecorator
def self.prepended(base)
base.prepend_before_action :load_vendor, only: :index
end
end
end
end
assign_extra_collection_params with search_params overrideThe assign_extra_collection_params method is no longer used in Spree 5.3. Instead, override the search_params method to add custom Ransack filters.
Before (Spree 5.2):
def assign_extra_collection_params
params[:q][:vendor_id_eq] = @vendor.id if @vendor.present?
end
After (Spree 5.3):
def search_params
result = super
result[:vendor_id_eq] = @vendor.id if @vendor.present?
result
end
The base ResourceController#search_params now processes date range params (created_at_gt, created_at_lt, etc.) and converts them to beginning_of_day. If you need end_of_day for _lt params, override search_params with custom handling:
def search_params
params[:q] ||= {}
params[:q][:s] ||= collection_default_sort if collection_default_sort.present?
if params[:q][:created_at_gt].present?
params[:q][:created_at_gt] = parse_date_param(params[:q][:created_at_gt])
end
if params[:q][:created_at_lt].present?
params[:q][:created_at_lt] = parse_date_param(params[:q][:created_at_lt])&.end_of_day
end
params[:q]
end
The old filter partial system (Spree.admin.partials.*_filters) has been replaced with built-in filtering in the Tables API. Remove any filter partials and configure filtering directly in table column definitions.
Before (Spree 5.2):
Spree.admin.partials.orders_filters << 'spree/admin/orders/vendor_filter'
After (Spree 5.3):
Spree.admin.tables.orders.add :vendor,
label: :vendor,
type: :custom,
filterable: true,
filter_type: :select,
ransack_attribute: :vendor_id_eq,
value_options: -> { Spree::Vendor.pluck(:name, :id) },
partial: 'spree/admin/orders/table/vendor_column'
See the Admin Tables documentation for more details.
If your extension includes admin views with CSS classes, update Bootstrap utility classes to Tailwind v4:
| Bootstrap | Tailwind v4 |
|---|---|
d-flex | flex |
d-none | hidden |
text-muted | text-gray-500 |
ml-2, mr-2 | ms-2, me-2 |
pl-2, pr-2 | ps-2, pe-2 |
font-weight-bold | font-bold |
If your extension uses Bootstrap tabs, migrate to the Stimulus tabs controller:
Before (Bootstrap):
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#tab1">Tab 1</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab1">Content</div>
</div>
After (Stimulus):
<div data-controller="tabs">
<ul class="nav nav-pills mb-4" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" data-tabs-target="tab" data-action="click->tabs#select" role="tab">Tab 1</a>
</li>
</ul>
<div data-tabs-target="panel" role="tabpanel" class="animate-fade-in">Content</div>
</div>
The progress_bar_component signature has changed:
Before (Spree 5.2):
<%= progress_bar_component(value, 0, 100) %>
After (Spree 5.3):
<%= progress_bar_component(value, min: 0, max: 100) %>
All table columns now require default: true to be visible by default:
Spree.admin.tables.vendors.add :name,
label: :name,
type: :string,
sortable: true,
default: true, # Required for column to be visible
position: 10