docs/developer/customization/quickstart.mdx
Spree is a flexible platform allowing you to customize every part of it to suit your business needs. This guide presents customization options in order of recommendation - start from the top and only move down if simpler options don't meet your needs.
| What you want to do | Recommended approach |
|---|---|
| Change store settings (currency, zones, languages) | Store Settings |
| Tweak Spree behavior globally | Configuration |
| React to model changes (sync, notifications) | Events & Subscribers |
| Notify external services | Webhooks |
| Swap core services (cart, checkout, etc.) | Dependencies |
| Add admin menu items | Admin Navigation |
| Add sections to admin forms | Admin Partials |
| Add searchable/filterable fields | Ransack Configuration |
| Add associations/validations to models | Decorators (last resort) |
There's a lot of Store settings you can change in the admin panel without touching the code.
Go to **Admin > Settings**
Global application configuration allows you to customize various aspects of Spree:
```ruby config/initializers/spree.rb
Spree.config do |config|
config.allow_guest_checkout = false
config.products_per_page = 20
end
```
Please see [Configuration](/developer/customization/configuration) section for more information.
<Info>
Events are the **recommended way** to add behavior when something happens in Spree, replacing the need for decorator callbacks.
</Info>
Spree's event system lets you subscribe to events like `order.completed`, `product.updated`, `payment.paid`, etc.:
```ruby app/subscribers/my_app/order_completed_subscriber.rb
module MyApp
class OrderCompletedSubscriber < Spree::Subscriber
subscribes_to 'order.completed'
def handle(event)
order = Spree::Order.find_by(id: event.payload['id'])
return unless order
# Sync to external service, send notification, etc.
ExternalService.notify_order_placed(order)
end
end
end
```
**Key benefits:**
- Loose coupling - your code doesn't depend on Spree internals
- Async by default - keeps requests fast
- Easier testing and upgrades
Please see [Events](/developer/core-concepts/events) section for more information.
Webhooks send HTTP POST requests to external URLs when Spree events happen:
- Order completed → Notify fulfillment system
- Product updated → Sync with PIM
- Customer created → Add to CRM
Configure webhooks in **Admin > Developers > Webhooks** or via the API.
Please see [Webhooks](/developer/core-concepts/webhooks) section for more information.
Spree allows you to replace core classes without modifying them:
```ruby config/initializers/spree.rb
Spree::Dependencies.cart_add_item_service = "MyCartAddItemService"
Spree::Dependencies.cart_remove_item_service = "MyCartRemoveItemService"
```
This is cleaner than decorating services because you provide a complete replacement rather than patching behavior.
Please see [Dependencies](dependencies) section for more information.
Spree provides declarative APIs for extending the admin without decorators or view overrides:
**Navigation API** - Add menu items:
```ruby config/initializers/spree.rb
Rails.application.config.after_initialize do
Spree.admin.navigation.sidebar.add :brands,
label: :brands,
url: :admin_brands_path,
icon: 'award',
position: 35
end
```
**Partials API** - Add sections to forms:
```ruby config/initializers/spree.rb
Spree.admin.partials.product_form << 'spree/admin/products/erp_section'
```
Please see:
- [Admin Navigation](/developer/admin/navigation) - For adding menu items
- [Admin Partials](/developer/admin/extending-ui) - For extending UI
- [Admin Tables](/developer/admin/tables) - For customizing list views
Instead of decorating models to add `ransackable_attributes`, use the Ransack configuration API:
```ruby config/initializers/spree.rb
Spree.ransack.add_attribute :product, :erp_id
Spree.ransack.add_association :product, :brand
```
Please see [Search & Filtering](/developer/core-concepts/search-filtering#extending-ransackable-configuration) section for more information.
Spree allows you to use your own authentication system instead of the default Devise-based one.
You can find more information in the [Authentication](authentication) section.
With Spree you can change the checkout flow to fit your business needs - add steps, remove steps, or change the order.
Please see [Checkout flow customization section](checkout) for more information.
<Warning>
Decorators should be used **only when no other option works**. They tightly couple your code to Spree internals and can break during upgrades.
**Do NOT use decorators for:**
- After-save callbacks → Use [Events](/developer/core-concepts/events) instead
- External service sync → Use [Webhooks](/developer/core-concepts/webhooks) instead
- Custom service logic → Use [Dependencies](dependencies) instead
- Admin UI changes → Use [Admin Extensions](#admin-extensions) instead
</Warning>
Decorators are still appropriate for structural changes:
```ruby app/models/spree/product_decorator.rb
module Spree
module ProductDecorator
def self.prepended(base)
base.belongs_to :brand, class_name: 'MyApp::Brand', optional: true
base.validates :external_id, presence: true
base.scope :featured, -> { where(featured: true) }
end
def full_title
"#{brand&.name} #{name}"
end
end
Product.prepend(ProductDecorator)
end
```
Please see [Decorators](decorators) section for more information.