Business logic is extended through workflow hooks rather than by replacing classes wholesale, and the admin is a React application with its own extension points rather than a Rails engine extended through partials.
Quick reference
Customizing the admin? This page covers the backend — models, API and business logic. The admin is a separate React application with its own extension points. See Dashboard Customization.
Store settings
Store settings
Best for: currency, markets, delivery zones, languages, and other business settings.Most day-to-day configuration is data, not code. Change it in the dashboard under Settings — no deploy required.
Configuration
Configuration
Workflow hooks
Workflow hooks
Best for: running your own logic inside a core flow — checkout completion, order cancellation, refunds, fulfillment creation.Register a handler against a hook key — Pass a class instead of a block for anything longer than a line:There are three families of hook:
Hooks are the headline extension point in Spree, so you don’t have to replace an entire class and keep your copy in sync with core forever.
<workflow key>.<hook name>:config/initializers/spree.rb
Hook keys are validated after boot, so a typo fails startup rather than silently never running.See Services & Workflows for the full list of flows and their hooks.
Events and subscribers
Events and subscribers
Best for: reacting after something happened — syncing to external systems, notifications, audit logging.Events or hooks? A hook runs inside the flow and can influence or block it. An event fires after the fact and cannot. Reach for an event unless you need to change the outcome.See Events.
app/subscribers/spree/order_placed_subscriber.rb
Checkout steps
Checkout steps
Best for: adding, removing or reordering checkout steps.Spree has no checkout state machine. A cart reports what it still needs, and you can add to that list:Or attach a requirement to an existing step:Registered requirements appear in the Cart API’s
config/initializers/spree.rb
requirements array, so your storefront renders them without duplicating any rules.See Carts.Providers
Providers
Best for: swapping a whole area of behavior for your own implementation.Spree exposes pluggable providers for the domains that vary most between businesses:
Each is a class you register and select on the relevant record, so no conditional code lands in core.See Fulfillments and Taxes.
Search and filtering
Search and filtering
Best for: making custom fields searchable and sortable in the dashboard and API.See Search & Filtering.
config/initializers/spree.rb
Authentication
Authentication
Best for: using your own user model or identity provider.Spree owns its authentication stack rather than depending on Devise, and separates the customer identity from the staff identity. Point Spree at your own classes with
Spree.customer_class and Spree.admin_user_class.See Authentication.Webhooks
Webhooks
Best for: notifying external services — ERPs, CRMs, fulfillment systems — without writing Ruby.Configure them in the dashboard under Settings → Webhooks, or through the Admin API.See Webhooks.
Dependencies
Dependencies
Best for: replacing an entire workflow or service with your own class.Prefer a hook when you only need to add behavior. Replace the class only when you need to change what the flow fundamentally does.See Dependencies.
config/initializers/spree.rb
Decorators
Decorators
Best for: adding associations, validations and scopes to Spree models. Use as a last resort.They remain appropriate for structural additions:See Decorators.
app/models/spree/product_decorator.rb
Choosing between hooks, events and dependencies
These three overlap, and picking the wrong one is the most common source of upgrade pain:1
Do you need to block or change the outcome?
Use a workflow hook. Only
validate hooks can stop an operation, and only they run early enough to do so safely — before money moves.2
Do you just need to know it happened?
Use an event subscriber. It’s decoupled, testable, and survives upgrades untouched.
3
Do you need the flow to do something fundamentally different?
Use dependencies to replace the class — and accept that you now own keeping it current.
Related
- Services & Workflows — the workflow model and every hook
- Carts — checkout requirements and completion
- Dashboard Customization — extending the admin

