Bases Serializer

Bases Serializer

Freeze an Obsidian Bases view into real, static Markdown — wrapped in sentinel comments so you can refresh it in place.

Obsidian 1.10.0+ License: MIT Buy Me a Coffee

--- Bases views are dynamic: they live inside a `.base` and don't exist as text, so their results are invisible to the Graph, Search, Publish, exports, and other tools. **Bases Serializer** writes the current result as ordinary Markdown, so: - links become **real links** (they show up in the Graph and in backlinks), - rows become **real tasks/lists** you can check off and query, - and the snapshot is **readable by anything** — no plugin required to read it. Re-run it any time to update the block in place.
A note containing a single base embed: ![[tasks.base#Today]] The same note after Serialize: the embed replaced by frozen Markdown
Before — a live base embed After — frozen Markdown, groups and all
## Install **Settings → Community plugins → Browse → "Bases Serializer"**, or view it in the [community directory](https://community.obsidian.md/plugins/bases-serializer). Requires Obsidian **1.10.0+** with **Bases** enabled.
Manual install / pre-release builds 1. Download `main.js`, `manifest.json`, and `styles.css` from the [latest release](../../releases/latest). 2. Copy them into `/.obsidian/plugins/bases-serializer/`. 3. Enable **Bases Serializer** in *Settings → Community plugins*. Or use [BRAT](https://github.com/TfTHacker/obsidian42-brat) with `pedroilidio/obsidian-bases-serializer` if you want to track versions ahead of a release.
## Quick start ### Embedded (recommended) 1. Add a **Serializer** view to your base (the view picker → *Serializer* ❄). 2. Embed that base in a note: `![[tasks.base#Today]]`. 3. Click **❄ Serialize here**. The frozen Markdown is inserted **directly below the embed**, inside a sentinel block keyed to the view. Keeping the snapshot next to the query it came from means the base is the source of truth: change the base, re-serialize, the note updates. ### Standalone 1. Open the base as its own tab. 2. Set a **Target note** in the view options (and optionally a **Section heading**). 3. Click **❄ Serialize now**. The block is written to that note — under the heading if you set one, otherwise appended at the end. ![The Serializer view in a base tab: the toolbar, the live preview, and the status line](./assets/preview.png) ## The toolbar | Control | What it does | | --- | --- | | **❄ Serialize now** / **Serialize here** | Writes the block. The label reflects the mode (standalone vs. embedded). | | **▾** | One-off actions: **Keep live view**, **Freeze · replace embed with text** (embedded only), **Copy Markdown**, **Open target note**, **Remove serialized block**. | | **Format** | The format picker (icon + label). Opens a menu with **Default (…)** plus `List`, `Checklist`, `Numbered`, `Table`. | - **Format vs. Default format.** The in-view **Format** picker is a per-view override; **Default (…)** clears it and follows the **Default format** set in the view options. The button always shows the *effective* format. - **Narrow panes.** When the view is squeezed, the button labels collapse to icons (matching the native Bases toolbar), and the status text ellipsizes. - **Copy Markdown** confirms with a toast. - **Command.** *Serialize the base in the active note* does the same as the button, for whichever Serializer base is in the current note — handy as a hotkey or for automation. ![The ▾ menu: Copy Markdown, Open target note, Remove serialized block](./assets/actions-menu.png) ![The Format picker: Default (Checklist) plus List, Checklist, Numbered, Table](./assets/format-menu.png) ## View options ### Standalone - **Target note** — where to write when the view isn't embedded. - **Section heading** — write the block under this heading in the target note (creates it if missing). ### Output - **Default format** — `List`, `Checklist`, `Numbered`, or `Table` (the fallback for the in-view picker). - **Link style** — `Wikilink [[Note]]` (default), `Markdown [title](path)`, or `Plain text`. - **Row template** — one template per row; overrides the format (see placeholders below). - **Heading / preamble** — a line emitted above the block (e.g. `## Tasks`). - **Sentinel key** — key for the block (defaults to the view name). ### Item details - **Item details** — `Hide` (default), `Inline` (same line), or `Sub-items` (nested bullets). - **Detail properties** — comma-separated ids/display names to show (empty = all columns). - **Details separator** — used by `Inline` (default ` · `; verbatim, include your own spaces). - **Show property names** — `Due: 2026-10-13` instead of bare values (default off). - **Trailing property** — appended to each checklist/numbered item as ` — value`. ### Behaviour - **Group headings** — emit a header per group from Bases' grouped results (default on). - **Group template** — header template with `{group}` and `{count}` (default `### {group}`). - **No-results text** — shown when there are no rows (default: the preview shows `(no rows)`). - **Preview max height** — cap on the in-view preview (default `50vh`). - **Auto-serialize (embedded)** — re-write (debounced) whenever the data updates. - **Freeze (replace embed with text)** — swap the live `![[base#view]]` for plain Markdown (no embed, no sentinels). #### Row template placeholders `{link}`, `{title}`, `{path}`, `{index}`, `{group}`, and any property by id (`{note.due}`) or display name (`{Due}`) — case-insensitive. ```markdown - {link} — {note.due} ({note.status}) ``` ## Output format Everything is wrapped in HTML-comment sentinels: ```markdown %% bases-serializer key="Today" format="checklist" %% - [ ] [[Notes/Foo]] — 2026-10-13 - [ ] [[Notes/Bar]] %% /bases-serializer %% ``` Only the block whose sentinel `key=` matches is replaced on re-serialize; everything else in the note is left untouched, and multiple blocks per note are supported. Set a custom **Sentinel key** to keep several independent snapshots. **Freeze** takes it further: instead of a sentinel block it drops the plain Markdown in place of the embed line itself — no `%%` markers, no `![[…]]` left. That's ideal for a final, "baked" note. ## Development ```bash npm install npm run dev # esbuild watch -> main.js npm run build # typecheck + bundle npm test # unit tests (sentinels, rendering) ``` Pure logic lives in `src/sentinels.ts` (block upsert/strip/freeze) and `src/render.ts` (formats, templates, groups) with **no Obsidian imports**, so it is unit-tested without the app. The Bases view wrapper lives in `src/main.ts`. See [ARCHITECTURE.md](ARCHITECTURE.md) for a guided tour — the file map, the data flow, how to add a format or an option, and the traps worth knowing about. ## Notes & limitations - Requires Obsidian **1.10.0+** (the public Bases API). `QueryController` is opaque, so this must be a **view type** — it can't serialize the built-in Table/Cards view directly. - Read-only with respect to the queried notes: it only ever writes the target note (or, on freeze, the note containing the embed). - `List` renders bare bullets; use `Checklist` for `- [ ]` tasks. ## Support If Bases Serializer saves you time, you can buy me a coffee — it keeps the ❄️ in shape.

Buy Me a Coffee

## Acknowledgements The code was written in pair-programming sessions with an AI assistant — **OpenCode** driving **DeepSeek V4.1 Flash** — under my direction. The design, scope, trade-offs and every product decision are mine; the implementation was largely the assistant's. `ARCHITECTURE.md` is the map I use to maintain it, and changes authored by the assistant carry an `Assisted-by:` trailer in git. ## License [MIT](LICENSE)