Skip to main content

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.
Because the definition carries the type and the label, the dashboard can build an editing form for it automatically, and your storefront gets a value it can trust the shape of.

Field types

Who can see it

storefront_visible decides whether a field ever leaves the back office:
storefront_visible: false genuinely withholds the field from the Store API — it is not hidden in the response, it is absent. Treat it as the boundary between what a customer may read and what only staff may.

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

Reading them from a storefront is an expand on whatever you already fetch:
Response
Each value arrives with its own label and type, so you can render a spec table straight from the array without hardcoding which fields exist.

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.
In the dashboard, fields with these flags become available in the product table’s column picker, sort menu and filter panel — off by default, so merchants opt in per column.

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 define custom.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.