ZhengYX703 downloadsMerged cells, multi-row headers, and row headers for readable Markdown tables.
Structural Tables adds merged cells, multi-row column headers, and row headers to ordinary pipe-table Markdown while keeping the source readable and portable.
Tables in blockquotes, callouts and list continuations retain their container when formatted or edited. Keep the same prefix on every table row. Move a nested table to the top level before upgrading it to Base.
Group columns under multi-row headers and merge row headers across related records. Three-line styling adds short rules below column groups while keeping the table body uncluttered.

Edit cells directly in Live Preview while keeping merged headers visible. The same table is shown here with the optional grid style; click a cell on desktop, or double-tap it on touch screens, to edit it in place.

Choose Follow theme, Grid, or Three-line table independently of table layout and density. General, Views, and Appearance keep the settings organized in three tabs.

< cell and merge up with an exact ^ cell.|| inside the delimiter row to mark row-header columns.| escaping for pasted Wiki links.structural-tables list and allow record notes to move or be renamed without leaving the Base; records need no plugin-specific identity property.Structural Tables requires Obsidian 1.12.7 or later and supports desktop Obsidian and Android. Upgrade to Base additionally requires Obsidian's Bases core plugin to be enabled. Structural Tables owns the meaning of exact <, ^, and delimiter || tokens inside a structural table. By default it warns once when an enabled table plugin is known to assign overlapping meanings to those tokens.
Install Structural Tables from Obsidian's Community plugins directory: open Settings → Community plugins → Browse, search for Structural Tables, select Install, and then enable the plugin.
For manual installation, download structural-tables-<version>.zip from the latest release and extract it into Vault/.obsidian/plugins/. The archive contains the structural-tables/ directory with main.js, manifest.json, and styles.css. Reload Obsidian, then enable Structural Tables under Community plugins.
< cell to merge left, an exact ^ cell to merge up, or one adjacent || inside the delimiter row to mark the columns on its left as row headers.<br>; Enter commits, Escape cancels, and Tab/Shift+Tab commits and moves through visible cells in source order, skipping merged placeholders.| Region | Sales | < |
| Quarter | Q1 | Q2 |
| --- || --- | --- |
| North | 10 | 12 |
| ^ | 8 | 11 |
All equal-width rows immediately before the delimiter are column-header rows. The || divider is internal, appears at most once, does not add a column, and makes columns to its left row headers. A merge must resolve to one top-left content cell, form a complete rectangle, and stay inside one header/data role region. Write \< or \^ for literal marker text.
Inside a cell, <br>, <br/>, and <br /> all render as a visual line break in Reading view and owned Live Preview tables. Hand-written spellings remain unchanged during formatting; Shift+Enter, the editor menu, multiline plain-text paste, and imported HTML use canonical <br>. Semantic HTML export emits real break elements, while text-oriented GFM, TSV, and CSV keep the tag as text. This is visual line-break syntax, not block content or a true multiline Markdown cell.
Spreadsheet clipboard fragments are supported even when the surrounding table tag is omitted. If the source supplies no semantic column headers, the first row and any rows joined to it by merged cells become the header group. Bold text alone does not identify row headers. Pasting a single spreadsheet cell into the cell editor preserves its visible line breaks without adding spreadsheet export quotes or a trailing blank line.
To paste into Word, right-click a rendered cell and choose Copy whole table for Word / HTML, or place the source cursor in the table and run Copy current table as HTML. Both copy the entire table, including merged cells and multi-row headers. Bold, emphasis, code, external links, and visual line breaks are exported as portable HTML; Wiki links and embeds use their visible labels. A readable tab-separated alternative is supplied to plain-text destinations. Copying while typing inside a cell retains the normal text-selection behavior. Word's paste option and document style can affect the final appearance; use the option that keeps source formatting.
Once a table uses any structural feature, every row must have exactly the delimiter width. Invalid structures keep their Markdown and show a diagnostic. Use Format current structural table for the canonical representation: the top-left cell stores content, the rest of the top row uses <, covered cells below use ^, and source pipes line up by terminal display width. Row-header ||, alignment markers, escaped pipes, Wiki links, code spans, and existing LF/CRLF/CR endings remain valid. The same alignment runs after an explicit edit, merge, split, row/column operation, or imported-table write; passive rendering never rewrites Markdown. GFM, TSV, and CSV conversion repeats merged values and joins multi-row column-header paths with / so the flattened result remains explicit.
In Live Preview, ordinary Markdown tables remain in Obsidian's native editor by default. Enable Take over ordinary Markdown tables to give unchanged GFM tables the same rendered widget, row/column handles, cell selection, in-place editor, context menu, layout, density, and alternating-row appearance as structural tables; disabling it restores native behavior immediately. Rendered tables follow native theme styling by default, preserve logical merged borders, and retain touch-sized interactive rows on coarse pointers. Optional density and style overrides do not alter source content. Handles overlay the table's outer gutter instead of shifting its content alignment; hovering a cell reveals only its row and column handles, while keyboard-focused or selected handles remain visible. An owned cell selection clears when focus or the editor cursor moves away. Pasting [[Target|Alias]] or ![[Image|Size]] into an owned cell automatically stores the table-safe forms [[Target\|Alias]] and ![[Image\|Size]]; existing escapes are not doubled. Operations that would discard non-empty content or break a merged rectangle are refused.
Promotion creates records under <host-folder>/_structural-table-records/<table-id>/. Each record uses a normal list Property:
structural-tables:
- stb_example
The directory is a creation inbox, not a membership boundary: moving or renaming a record note does not change its membership. Existing records stay where the user placed them when the host note moves. The native Base New action uses Obsidian's creation location. Structural Tables does not move native-created, copied, imported or synchronized notes. Create record for current promoted Base remains available from the command palette and context menu. Use Restore table from current promoted Base to recover the original table from _promotion.json; generated notes are deliberately kept. This restores the original snapshot, not subsequent edits to Base records. Promotion preserves every non-empty header, including numeric and leading-zero headers, as its Property name and uses column_n only for a blank header. Duplicate or reserved names receive a numeric suffix. Imported cell values remain strings, so leading zeroes and identifiers are unchanged.
Existing Bases that use structural_table_ids remain supported. Run Migrate legacy Structural Tables Base properties… to preview each affected file and its intended changes, replace the old membership Property and Base filters, and optionally remove retired structural_record_id values only from notes with valid Structural Tables membership. Cleanup is off by default, and its count plus per-file actions update when the toggle changes. Nothing migrates at startup; invalid or conflicting old/new membership Properties stop the migration without overwriting them, and rollback preserves unrelated edits made concurrently.
If a cell commit is rejected, its editor stays open. If the table changes externally or the view is rebuilt before a draft is saved, a recovery dialog keeps the draft available for copying. Close the dialog and use Recover interrupted cell drafts to reopen it, or explicitly discard a draft. Recovery buffers last until Obsidian restarts or the plugin reloads; copy them before either action. Paste preserves fragment whitespace and applies table escaping to the complete cell on commit.
The settings page follows Obsidian's native controls and has General, Views, and Appearance tabs. General controls HTML-table paste conversion and startup conflict warnings. Views includes the default-off ordinary-table takeover alongside Reading view, Live Preview, and diagnostics. New installations follow the theme for table style, width, and density. Appearance offers Follow theme, Grid, and Three-line table styles, independent content-left/content-center/fill-text-width layouts, comfortable/compact spacing, and alternating rows; language can Follow Obsidian or use English/Simplified Chinese.
Plugin-owned tables stay within the text column, including under theme wide-table classes; wide content scrolls inside the table. Native ordinary tables continue to follow Obsidian and the theme. Enable Take over ordinary Markdown tables to apply the same controls to both. Existing explicit width and density preferences are preserved; choose Follow theme for native spacing. Three-line style draws the top rule, one separator below the complete header group, and the bottom rule, without vertical or body-row rules. Multi-level column groups also have short, inset rules below spanning titles with child headers; these never cross row-spanning headers. HTML copying uses portable grid or three-line borders when that style is selected; theme mode exports a neutral borderless table. Exported group rules use real cell borders instead of inset decorations for compatibility. Width, density, alternating rows, and theme colors stay in Obsidian. Appearance settings never change Markdown.
Structural Tables does not support formulas, per-cell styling, block-level or true multiline Markdown cell content, captions, numbering, source attributes for repeated headers, or automatic rich-text-to-Markdown conversion inside imported HTML cells. Imported HTML content becomes plain text plus canonical <br> visual breaks. Base upgrade flattens layout structure into properties: multi-row header paths are joined with /, row headers become ordinary properties, and merged row-header values repeat per record. A merged data cell blocks confirmation and identifies its location until it is split. Only the plugin's explicit record-creation command chooses the host inbox; no global file-creation listener moves existing notes. Recovery requires the generated _promotion.json to remain at the path recorded in the Base block. The parser deliberately refuses ambiguous or nonrectangular merges.
Structural Tables works locally. It does not make network requests, load remote assets, collect analytics, or send note content anywhere. Rendering never changes source Markdown; in-place and menu edits are explicit and validated before replacement. Promotion creates only the previewed local record notes and recovery manifest. A failed promotion moves its newly created table-specific directory to the configured Obsidian trash.
Use Node 24.19.0 and npm 11.17.0.
npm ci
npm run check
Developer references:
Never post real private Vault paths, note content, credentials, or personal information publicly.
MIT © ZhengYX