Overview
Sooner or later you need to store something Spree doesn’t have a field for. A fabric composition. A care instruction. A gift message. An ID from the system you sync with. Custom fields are how you do that without changing the database. You declare a field once — its name, its type, who can see it — and from then on it can be set on any record of that kind, edited in the dashboard, returned by the API, and searched or filtered like any built-in field.Definitions and values
There are two halves, and it helps to keep them straight:- A definition is the declaration: “products have a Material, it is short text, shoppers may see it.” You create it once.
- A custom field is one record’s answer: this product’s material is
100% Cotton.
Field types
Who can see it
storefront_visible decides whether a field ever leaves the back office:
Declaring a field
namespace keeps groups of fields apart, so an integration’s product_id never collides with yours. Together they form the key you’ll see in responses: properties.material. Both are normalized to snake_case.
Definitions can also be managed in the dashboard under Settings → Custom Fields, which is where merchants usually add them.
Setting and reading values
Response
Searching, sorting and filtering
A custom field can take part in product listings — but only if you ask, because indexing everything by default would be wasteful.
Setting either flag also makes the field filterable. Filter and sort with the field’s
cf_ key, on both APIs:
Use
i_cont for case-insensitive matching. A comparison that doesn’t suit the field’s type is ignored rather than rejected, so a stale filter in a saved view can’t break a page.
If you use Meilisearch, re-index after changing these flags so the new fields are picked up. Substring matching (
i_cont, cont, start, end) is a database-provider feature; Meilisearch handles equality, ranges and presence. See Search & Filtering.What can carry custom fields
Most things you’d want to annotate: products, variants, orders, line items, customers, categories, payments, fulfillments, gift cards and store credits, among others.Definitions belong to a store
A definition is owned by the store it was created in. Each store keeps its own set, so two stores can both definecustom.material without colliding, and
neither can read or edit the other’s. Everything that reads the schema — the
dashboard, CSV exports and imports, and product sorting and filtering — reads
the current store’s definitions.
Custom fields or metadata?
Spree has two ways to store your own data, and they are not competing — they solve different problems.
Put it simply: custom fields are for people, metadata is for machines. If a merchant should type it, define a custom field. If it is a sync token or an external ID that only your integration reads, use metadata.
Related
- Metadata — the machine-readable alternative
- Products — the most common place for custom fields
- Search & Filtering — how filters and search work
- Admin SDK — managing definitions and values in TypeScript

