docs/developer/contributing/developing-spree.mdx
Spree is a big monorepo. For a faster clone, use a partial clone which downloads file contents on demand while keeping full commit history for git log and git blame:
git clone --filter=blob:none https://github.com/spree/spree.git
You need Node.js 20+ to run the workspace scripts (including pnpm server:setup, which both backend and TypeScript contributors use). Install it via nvm, mise, fnm, or your package manager (brew install node on macOS).
pnpm is provisioned automatically via Corepack (bundled with Node) — the repository pins its version in package.json, and the first pnpm command will fetch the matching release. If Corepack is disabled in your environment, run corepack enable once.
Spree is a monorepo with three main areas:
spree/ — Ruby gems (core, api, admin, emails) distributed as separate packages via RubyGemspackages/ — TypeScript packages (SDKs, CLI, project scaffolding, docs)server/ — A Rails application cloned from spree-starter that mounts the Spree gems (not checked in — run pnpm server:setup to create it)The Spree Rails engines live inside spree/ and are distributed as separate gems (Ruby packages installed via Bundler):
| Engine | Gem | Description |
|---|---|---|
core | spree_core | Models, services, business logic |
api | spree_api | REST APIs |
admin | spree_admin | Admin dashboard |
emails | spree_emails | Transactional emails |
All Spree models, controllers and other Ruby classes are namespaced by the Spree keyword, eg. Spree::Product. This means that those files are also located in spree sub-directories eg. app/models/spree/product.rb.
The server app is not checked into the monorepo. It's cloned from spree-starter on first setup, with SPREE_PATH=.. automatically configured so it uses your local Spree gems.
Step 1: Clone the server app
pnpm server:setup
This clones spree-starter into server/ and sets SPREE_PATH=.. in server/.env so it uses your local Spree gems.
Step 2: Configure and run
cd server
# Edit .env if needed (e.g. DATABASE_USERNAME, DATABASE_HOST, DATABASE_PORT)
bin/setup # installs Ruby (via mise), Postgres/Redis/Meilisearch (via brew), gems, prepares database
bin/dev # starts Rails + Sidekiq + CSS watchers via overmind
bin/setup installs system services natively (PostgreSQL, Redis, Meilisearch) via brew bundle on macOS or apt-get on Linux. If you'd rather run those services in Docker, start them from the repo root before running bin/setup:
docker compose up -d postgres redis meilisearch
Use bin/setup --reset to drop and recreate the database.
The app runs at http://localhost:3000. Admin Panel is at http://localhost:3000/admin.
Step 3 (optional): Load sample data
To populate your store with sample products, customers, orders, and configuration:
pnpm server:load_sample_data # from repo root
# or, from server/: bin/rails spree:load_sample_data
Each engine has its own test suite. First install the shared dependencies at the spree/ level, then navigate into the specific engine to set up and run its tests:
# 1. Install shared dependencies
cd spree
bundle install
# 2. Set up and run tests for a specific engine (e.g. core)
cd core
bundle install
bundle exec rake test_app
bundle exec rspec
Replace core with api, admin, or emails to test other engines.
By default engine tests run against SQLite3. To run against PostgreSQL, set the DB environment variable:
DB=postgres DB_USERNAME=postgres DB_PASSWORD=password DB_HOST=localhost bundle exec rake test_app
Run a single spec file:
cd spree/core
bundle exec rspec spec/models/spree/state_spec.rb
Run a specific test by line number:
cd spree/core
bundle exec rspec spec/models/spree/state_spec.rb:7
For faster test runs on multi-core machines, you can use the parallel_tests gem to distribute spec files across multiple CPU cores.
After setting up the test app, create databases for parallel workers:
cd spree/core
bundle exec rake parallel_setup
Then run specs in parallel:
bundle exec parallel_rspec spec
You can also specify the number of workers:
bundle exec rake "parallel_setup[4]"
bundle exec parallel_rspec -n 4 spec
After schema changes, re-run bundle exec rake parallel_setup to update the worker databases.
The Admin Panel uses feature specs that run in a real browser via chromedriver. You only need this if you're working on admin UI changes.
Install chromedriver on macOS:
brew install chromedriver
You may notice that your Spree store runs slower in development environment. This is caused by disabled caching and automatic reloading of code after each change.
Caching is disabled by default. To turn on caching please run:
cd server && bin/rails dev:cache
You will need to restart rails server after this change.
TypeScript developers don't need Ruby installed. Docker Compose from the repository root starts the backend using a prebuilt image:
docker compose up -d
This boots PostgreSQL, Redis, Meilisearch, and the Spree backend automatically (no pnpm server:setup needed). The API is available at http://localhost:3000.
Then install dependencies and start all packages in watch mode:
pnpm install
pnpm dev
| Package | Path | Description |
|---|---|---|
@spree/sdk | packages/sdk | TypeScript SDK for the Spree Store API |
@spree/cli | packages/cli | CLI for managing Spree Commerce projects |
create-spree-app | packages/create-spree-app | Project scaffolding (npm create spree-app) |
@spree/docs | packages/docs | Developer documentation for AI agents and local reference |
Internal packages (@spree/admin-sdk, @spree/sdk-core) are also part of the workspace but are not published to npm.
Run from the repository root — Turborepo orchestrates tasks across all packages:
| Command | Description |
|---|---|
pnpm dev | Start Docker backend + watch mode for all packages |
pnpm build | Build all packages (Turbo resolves dependency order) |
pnpm test | Run tests in all packages |
pnpm lint | Lint all packages |
pnpm typecheck | Type-check all packages |
pnpm clean | Remove build artifacts |
You can also run commands in a single package:
pnpm --filter @spree/sdk test:watch
pnpm --filter @spree/sdk console
pnpm --filter @spree/sdk generate:zod
Tests use Vitest with MSW for API mocking at the network level.
TypeScript types in packages/sdk/src/types/generated/ are auto-generated from the Rails API serializers using typelizer. To regenerate after changing serializers:
cd spree/api
mise install --yes # to install Ruby if you don't have it already
bundle install # to install dependencies
bundle exec rake typelizer:generate
After regenerating types, update the Zod schemas:
pnpm --filter @spree/sdk generate:zod
Published packages use Changesets for version management. Each package owns its own .changeset/ folder, so changesets must be created from inside the package directory:
cd packages/sdk # or packages/cli, packages/create-spree-app
pnpm changeset
This creates a changeset file describing your changes. Commit it with your PR. When merged to main, a GitHub Action creates a "Version Packages" PR that bumps the version and publishes to npm.
Consistent code style is enforced via automated linters. Please make sure your changes pass linting before submitting a PR.
Ruby: We use RuboCop for Ruby code. The configuration lives in server/.rubocop.yml and is shipped with spree-starter, so it's only available after running pnpm server:setup. Run it from the server/ directory:
cd server
bundle exec rubocop
To auto-fix correctable offenses:
bundle exec rubocop -a
TypeScript: We use Biome for linting and formatting TypeScript code. Run it from the repository root:
pnpm lint
Create a new branch for your changes. Do not push changes to the main branch. Branch names should be human-readable and informative:
fix/order-recalculation-total-bugfeature/my-new-amazing-featureKeep your commit history meaningful and clear. Each commit should represent a logical unit of work. This guide covers this well.
A few tips:
Fixes #<number> in the commit message or PR description to auto-close it on mergeWe use GitHub Actions to run CI.
Push your changes to a topic branch in your fork of the repository.
git push origin fix/order-recalculation-total-bug
Create a Pull Request against the main branch.
Wait for CI to pass.
Wait for Spree Core team code review. We aim to review and leave feedback as soon as possible.
To help us review your PR quickly:
@spree/sdk, @spree/cli, or create-spree-app). Run pnpm changeset from inside that package's directory — each package owns its own .changeset/ folder.We use GitHub Issues to track bugs. Before filing a new issue, please search existing issues to avoid duplicates.
When reporting a bug, please include:
We have an issue template that will guide you through this.
Issues that are open for 14 days without actionable information or activity will be marked as stale and then closed. They can be re-opened if the requested information is provided.
Spree comes with an AGENTS.md file that instructs coding agents like Claude Code or Codex to help you with your development.
We also have an MCP server built on top of our Documentation website to help you with your development.
Add this URL to your AI tools:
https://spreecommerce.org/docs/mcp
In Claude Code you need to go to Connectors settings and add the URL.
Thank you for participating in Open Source and improving Spree - you're awesome!