Typed properties
A type says what a thing has: a task has a status and a due date, a book has an author and a rating. Declare one and Subspace gives you a small form for those fields, warns you when a value does not fit, and makes the values queryable.
The values themselves stay where you can see and edit them: as ordinary key:: value
child bullets in the outline. The form is a convenience over those bullets, not a
separate store, so you can always type a property by hand instead.
Declaring a type
Section titled “Declaring a type”Create a page inside the types directory and give it a metadata.type object. Open the
metadata editor with ⌘⇧M and write, for example:
{ "type": { "tag": "task", "fields": [ { "name": "status", "kind": "select", "options": ["todo", "doing", "done"], "required": true }, { "name": "due", "kind": "date" }, { "name": "owner", "kind": "ref" } ], "defaults": { "status": "todo" } }}| Key | Meaning |
|---|---|
tag |
The #tag that binds this type to a bullet. A leading # is optional. |
namespace |
A directory slug. Every page directly in that directory gets these fields. |
fields |
Each has a name and a kind: text, number, date, boolean, select, ref, or relation. select also needs options; any field can be required. |
defaults |
Per-field starting values, shown as placeholders. |
strict |
false by default. See validation. |
Using a type
Section titled “Using a type”-
Tag a bullet
Write
#taskanywhere in a bullet. That bullet is now a typed node, and a row of property chips appears beneath it. -
Or put a page in a namespace
If the type declares a
namespace, every page directly inside that directory gets a Properties panel at the top, with no tagging needed. -
Fill in the fields
Use the form, or write the child bullets yourself:
status:: doing. Both produce exactly the same thing. Clearing a field in the form deletes the bullet.
A ref or relation field accepts a page title and wraps it as [[Title]] for you.
Those references become real links, so a page
shows up in the backlinks of anything that points at it through a property.
Querying by property
Section titled “Querying by property”Typed properties are queryable view fields: filter, sort, and group by
status or due the way you would by any other field. Numbers compare as numbers and
dates as dates, rather than as text. A { type: 'task' } source selects the tagged
bullets themselves, and matches on the type page’s slug, its title, or its tag.
Validation is advisory by default
Section titled “Validation is advisory by default”An out-of-range value never blocks your edit. It is accepted, and a Property warning
banner appears on the page naming the field and what it expected, for example
task.status must be one of todo, done. Fix the value and the warning clears.
Set "strict": true on a type to reject invalid writes outright instead.
How this relates to plugin page types
Section titled “How this relates to plugin page types”Plugins can also declare types, in their manifest, with a JSON Schema. Both kinds show up in the same Properties panel, but they are not the same mechanism, and the differences matter:
Types you write in types/ |
Types a plugin declares | |
|---|---|---|
Binds bullets by #tag |
Yes | No, pages only |
| Binds pages by directory | Yes, via namespace |
Yes |
| Where values live | key:: value bullets in the outline |
The page’s JSON metadata |
| Can reject invalid writes | Yes, with strict |
No, always advisory |
If both apply to a page, yours wins.