Schema
unlistedby rephlex00
One source of truth for typed notes: define object types once, get auto-filing, per-type creation commands, and automatic backlinks.
Schema
A typed-note workflow for Obsidian. Define object types in the plugin settings; get auto-reshelve on type: change, native lookups (frontmatter or inline blocks), creation commands per type, and one source of truth for schema, folder, filename, prompts, and visual defaults.
Why
A typical typed-note workflow in Obsidian today threads through four plugins:
| Concern | Plugin |
|---|---|
What fields does a person have? | Metadata Menu (fileClass) |
Where do person files live? | Workflow Objects (pathMappings) |
How does a new person get created? | Templater + QuickAdd |
What happens when you change type:? | Two manual commands |
Schema collapses all of that into one source of truth (the plugin's Settings → Schema tab) and ships the lifecycle as native subsystems.
Requirements
- Obsidian 1.4.0 or newer.
- Dataview is optional. It's only needed for Custom lookups that go beyond the built-in query subset (see Lookup query runtime); everything else works without it.
Installation
From the community directory (once Schema is listed): Settings → Community plugins → Browse, search for "Schema", Install, then Enable.
Manually: download main.js, manifest.json, and styles.css from the latest release, put them in <your-vault>/.obsidian/plugins/schema/, then enable Schema under Settings → Community plugins.
Defining a type
Settings → Schema → "+ Add type". Each type expands inline:
- Basics: name, extends, folder, filename template, tags
- Defaults: per-type values for whichever frontmatter keys you've designated as auto-refreshed (icon and color by default; configurable globally)
- Fields: inline-expand rows, type-aware widgets (Input, Number, Boolean, Select, MultiFile/File with target type, Date/Time, etc.), with
promptOnCreatelabels for the create flow - Backlinks: per-property auto-generated reverse links (set a Backlinks name on any File/MultiFile property and the target object type gets a frontmatter list of every note that links to it)
- Custom lookups: hand-written Dataview queries when a Backlink can't express what you need
That's it. No YAML files to edit, no JSON to hand-write.
Lifecycle
When you change a note's type: value (typing it directly or applying a #type/<name> tag), the plugin atomically:
- Moves the file to the new type's folder
- Strips frontmatter keys not in the new schema
- Adds empty placeholders for missing required fields
- Resets auto-refreshed fields (icon, color) from the new type's defaults
When you run Schema: New <type> from the command palette:
- The plugin prompts for any field with
promptOnCreate - Renders the filename template
- Places the file in the configured folder
- Opens it
Computed lists: Backlinks and Custom lookups
Schema gives you two ways to produce a computed list on a note:
Backlinks (the common case)
A backlink is auto-created when you set a Backlinks name on a global
property of type File or MultiFile. Example: person.organization is a
MultiFile targeting organization. Set its Backlinks name to members,
and every organization note gets a members: list of every person that
links to it.
You do not write a query. The list updates whenever a person's
organization: value changes. Each object type's editor shows a card per
Backlink with a sample of the matching notes and a button to jump back to
the property that defines it.
By default the result is written into the target note's YAML frontmatter:
members:
- "[[People/Alice]]"
- "[[People/Bob Smith]]"
Each Backlinks card has an Show in frontmatter (default) / Show as inline block toggle that flips the destination per-backlink without losing the auto-update behavior.
Custom lookups (advanced)
For lists that can't be expressed as a backlink (date-windowed queries, multi-criteria filters, etc.), you can hand-write a Dataview query in the Custom lookups section of any object type. Each row chooses where the result lands:
- Property: written into the note's YAML, same shape as Backlinks.
- In the note body: rendered live via a
schema-lookup <name>code block you place in the note body:
## Recent moments
```schema-lookup
recent_moments
```
The block re-renders as data changes; no frontmatter writes, no git noise.
Auto-refreshed fields
A global setting (autoRefreshedFields, default ["icon", "color"]) lists frontmatter keys that always reset to the type's defaults whenever a note is reshelved or retyped. Add summary if you want a per-type default summary; add any custom key you want pinned to the type. The Defaults section of each type editor shows an input per key in this list.
Commands
| Command | Description |
|---|---|
Schema: New <type> | One per type with a folder set. Prompts → filename → folder → open. |
Schema: Edit field | Pick a field on the active note (fuzzy), open type-aware editor. |
Schema: Reshelve and clean active file | Manual reshelve+clean for one file. |
Schema: Refresh frontmatter lookups (vault-wide) | Re-run all frontmatter-mode lookups across the vault. |
Schema: Show loaded types | Console summary. |
Settings
| Setting | Default | Description |
|---|---|---|
| Auto-reshelve on type change | on | When set, editing a note's type: value triggers reshelve+clean automatically. |
| Auto-refreshed frontmatter fields | ["icon", "color"] | Comma-separated list of keys that get reset to schema defaults on every type change. |
The Settings tab also shows every loaded type with editable folder / filename / icon / color / fields / lookups, plus any validation issues.
Lookup query runtime
If Dataview is installed, queries execute via its JS API.
If not, a built-in fallback handles a restricted-but-real-world-useful subset:
dv.pages('"FOLDER"').filter(callback)- entry shape- Inside callback: frontmatter access,
current.file.path/current.file.name,.some(),===,&&,||,.includes, string slicing dv.luxon.DateTime.fromFormat(...).toFormat(...)shim for the week-code arithmetic the temporal lookups need
Queries that go outside this subset throw a clear error; no silent failures.
Cookbook
Lightweight CRM
Two types - person and interaction - give you a contact list with a running
log of every touchpoint, and each person's note automatically lists their
interactions in reverse-chronological order.
person:
folder: People
filename: {{firstname}} {{lastname}}
fields:
firstname (Input, promptOnCreate)
lastname (Input, promptOnCreate)
company (Input)
email (Input)
interaction:
folder: Interactions/{{date:YYYY}}
filename: {{date:YYYYMMDD-HHmm}} {{summary|slug}}
fields:
summary (Input, promptOnCreate)
when (DateTime)
with (MultiFile, target: person, Backlinks name: interactions)
channel (Select: email, call, in-person, slack)
The Backlinks name interactions on interaction.with gives every
person note an interactions: list of the interactions that mention them.
No manual maintenance.
Daily journal with people + moments
Daily notes capture quick reflections; moments are richer per-event records that link to people. Backlinks mean each person's note shows every moment they appear in.
moment:
folder: Moments/{{date:YYYY}}
filename: {{date:YYYYMMDD-HHmm}}
fields:
summary (Input, promptOnCreate: "What happened?")
people (MultiFile, target: person, Backlinks name: moments_with_me)
place (File, target: place)
daily:
folder: Daily/{{date:YYYY}}/{{date:MM}}
filename: {{date:YYYY-MM-DD}}
fields:
weather (Input)
mood (Cycle: bad, ok, good, great)
Run Schema: New moment and the plugin prompts for the summary, then opens the
note in Moments/2026/20260602-1543.md. Add [[Jane Doe]] to the people
field - Jane's note's moments_with_me list updates automatically on the next
metadata refresh.
Zettelkasten
Two types - permanent for evergreen notes and literature for source-tied
notes - with a references field that wires both back to source material.
permanent:
folder: Zettel
filename: {{date:YYYYMMDD-HHmm}} {{title|slug}}
fields:
title (Input, promptOnCreate)
references (MultiFile, target: literature, Backlinks name: cited_by)
literature:
folder: Literature
filename: {{author}} - {{title}}
fields:
title (Input, promptOnCreate)
author (Input, promptOnCreate)
year (Number)
url (Input)
Each literature note's cited_by list updates automatically when a permanent
note references it.
Dev / build
git clone https://github.com/rephlex00/Schema
cd Schema
npm install
npm run build # one-shot build
npm run dev # esbuild watch
npm test # vitest
npm run deploy # build + copy main.js, manifest.json, styles.css to dev vault
npm run deploy copies the build into a local vault for testing. Set
OBSIDIAN_PLUGIN_DIR to your vault's plugin folder first, e.g.:
export OBSIDIAN_PLUGIN_DIR="/path/to/YourVault/.obsidian/plugins/schema"
npm run deploy
Releasing
Every push and PR to main runs the build and the test suite via CI. To cut a
release, bump the version and push a matching tag:
npm run version:bump # syncs manifest.json, package.json, versions.json
git commit -am "Release <version>"
git push
git tag <version> # tag must equal manifest.json version, no "v" prefix
git push origin <version>
The tag push triggers the release workflow, which builds and creates a draft
GitHub release with main.js, manifest.json, and styles.css attached.
Review the draft and publish it.
License
MIT.
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.