CodeMirror extension that turns Markdown tables into interactive components
Features • Installation • Usage • Customization • Credits • License
A powerful table component that seamlessly integrates with CodeMirror
- Renders Markdown table text as interactive HTML tables
- Autoformats and prettifies Markdown tables while editing
- Renders custom styles and custom themes in code and/or CSS
- Renders cells with multi-line text
- Autocorrects line breaks around tables while editing to keep them separate from surrounding text
- Integrates with native CodeMirror undo history
- Integrates with native CodeMirror search
- Implements touch-friendly controls and editing for mobile browsers
- Autoinserts line breaks around pasted tables when necessary to keep them on their own lines
- Autoescapes pipe characters in cell text
- Create line breaks with Shift+Enter (inserts a
<br>)
- Move around the table with Up,Down,Left,Right
- Move to the next cell with Tab
- Append a new row by moving past the last cell
- Move to the previous cell with Shift+Tab
- Prepend a new row by moving before the first cell
- Move to the cell below with Enter
- Append a new row by moving past the last row
- Select a group of cells by Clicking and Dragging
- Scroll the table by Dragging past the editor edge
- Resize the cell selection by Shift+Clicking and Dragging
- Resize the cell selection with Shift+Up,Shift+Down,Shift+Left,Shift+Right
- Clear selected cells with Backspace,Delete
- Delete selected empty rows/columns with Backspace,Delete
- Copy selected cells as a Markdown table with Ctrl+C/Cmd+C
- Cut selected cells as a Markdown table with Ctrl+X/Cmd+X
- Paste a Markdown table into selected cells with Ctrl+V/Cmd+V
- Select excess cells to duplicate the pasted Markdown table across the extra cells
- Move row/column by Clicking Row/Column and Dragging to a new location
- Scroll the table by Dragging past the editor edge
- Insert a row by Clicking Row Border
- Insert one or more rows by Clicking Row Border and Dragging Down
- Insert a column by Clicking Column Border
- Insert one or more columns by Clicking Column Border and Dragging Right
- Delete one or more empty rows by Clicking Row Border and Dragging Up
- Delete one or more empty columns by Clicking Column Border and Dragging Left
- Append a row by Clicking Table Bottom Button
- Append one or more rows by Clicking Table Bottom Button and Dragging Down
- Append a column by Clicking Table Right Button
- Append one or more columns by Clicking Table Right Button and Dragging Right
- Append a row and column by Clicking Table Bottom-Right Button
- Append one or more rows and columns by Clicking Table Bottom-Right Button and Dragging Down-Right
- Trim one or more empty rows by Clicking Table Bottom Button and Dragging Up
- Trim one or more empty columns by Clicking Table Right Button and Dragging Left
- Trim one or more empty rows and columns by Clicking Table Bottom-Right Button and Dragging Up-Left
- Open row/column menu by Clicking Row/Column
- Sort by column
- Align a column to left, center, or right
- Unalign a column
- Add a row/column
- Move a row/column
- Duplicate a row/column
- Clear a row/column
- Delete a row/column
A convenient autocomplete menu for creating tables
- Pops up a CodeMirror autocompletion menu after typing
|on an empty line - Displays a customizable list of table size options
A command that smartly inserts a new Markdown table
- Inserts an empty Markdown table at the cursor or replaces the current selection
- Adds line breaks around table when necessary to prevent overlap with surrounding text
npm install codemirror-markdown-tablesimport { EditorView } from "@codemirror/view"
import { markdownTables } from "codemirror-markdown-tables"
// Add Markdown tables extension to CodeMirror
new EditorView({
extensions: [markdownTables()],
})import { autocompletion } from "@codemirror/autocomplete"
import { markdown, markdownLanguage } from "@codemirror/lang-markdown"
import { EditorView } from "@codemirror/view"
import { markdownTableAutocompleter } from "codemirror-markdown-tables"
// Create markdown language with GitHub-flavored Markdown support
const markdownLanguageSupport = markdown({ base: markdownLanguage })
// Create Markdown tables autocomplete extension (merges with other Markdown autocomplete extensions)
const markdownTableAutocompletion = markdownLanguageSupport.language.data.of({
autocomplete: markdownTableAutocompleter(),
})
// Add all extensions to CodeMirror
new EditorView({
extensions: [autocompletion(), markdownLanguageSupport, markdownTableAutocompletion],
})import { EditorView, keymap } from "@codemirror/view"
import { insertEmptyMarkdownTable } from "codemirror-markdown-tables"
// Create key binding that inserts a 2x2 table
const insertTableKeyBinding = {
key: "Alt-Mod-t",
run: insertEmptyMarkdownTable(),
}
// Wrap the key binding inside a keymap and add the extension to CodeMirror
new EditorView({
extensions: keymap.of([insertTableKeyBinding]),
})Optional config passed into the markdownTables() function to customize the core extension
| Property | Values | Default (when omitted or undefined) | Description |
|---|---|---|---|
theme |
TableTheme,{ light: TableTheme; dark: TableTheme } |
{ light: TableTheme.light, dark: TableTheme.dark } |
Color scheme for the table When set to a light and dark theme, the theme adjusts based on the CodeMirror light and dark mode configuration (not CSS prefers-color-scheme)When set to a single theme, the theme applies in both modes (i.e. theme: SomeTheme is equivalent to theme: { light: SomeTheme, dark: SomeTheme }) |
style |
TableStyle |
TableStyle.default |
Fonts and other styles for the table |
selectionType |
"codemirror", "native" |
"codemirror" |
Text cursor and selection implementation for the table cell editor When set to "codemirror", the editor uses CodeMirror's implementationEssentially, the CodeMirror editor embedded inside cells enables the drawSelection extension along with some CSS that hides the browser's native cursor and selectionWhen set to native, the editor uses the browser's implementationSpecify native only if drawSelection isn't enabled in the root CodeMirror editor (it's enabled by default with basicSetup and minimalSetup) |
handlePosition |
"outside", "inside" |
"outside" |
Position of the row and column header grips When set to "outside", handles appear beyond the top/left edge of the tableThis requires a sufficient left margin to keep the table edge aligned with the root CodeMirror editor edge, but it's much easier to click and drag the handles, especially on mobile When set to "inside", handles appear on the top/left table border itselfThis requires no extra left margin, but it's difficult to click and drag the handles, especially on mobile |
lineWrapping |
"wrap"", "nowrap" |
"wrap" |
Wrapping mode of the table When set to "wrap", wraps long table cell textEssentially, the CodeMirror editor embedded inside cells enables the lineWrapping extension along with the CSS "word-break": "normal", "overflow-wrap": "break-word"When set to "nowrap", the editor does not wrap long table cell textThe editor sets the CSS to white-space: "pre" |
extensions |
Extension[] |
[] |
Extensions for the table cell editor The CodeMirror editor embedded inside cells (not the root CodeMirror editor) enables the given extensions The table cell editor doesn't automatically inherit root CodeMirror editor extensions Instead, specify basic editor extensions like highlightWhitespace here to enable them inside cellsKeyboard shortcuts specified here execute actions on the CodeMirror editor embedded inside cells, rather than the root CodeMirror editor They operate on the text inside the cell in isolation, rather than the text of the document as a whole Specify KeyBindings from defaultKeymap or similar here to enable basic shortcuts inside the cell editordefaultKeymap defines shortcuts like Ctrl+A/Cmd+A which should select all the cell text rather than all the document textConversely, specify KeyBindings from historyKeymap and searchKeymap in globalKeyBindings instead, since these keyboard shortcuts operate on the root CodeMirror editor and the document as a whole |
markdownConfig |
MarkdownConfig |
{} |
Markdown language configuration for the table cell editor The CodeMirror editor embedded inside cells calls the markdown() function in @codemirror/lang-markdown with the specified optionsThe table cell editor doesn't automatically inherit the root CodeMirror Markdown language configuration See @codemirror/lang-markdown for descriptions |
globalKeyBindings |
KeyBinding[] |
[] |
Keyboard shortcuts for the table cell editor that delegate to the root CodeMirror editor Keyboard shortcuts specified here execute actions on the root CodeMirror editor, rather than the CodeMirror editor embedded inside cells They operate on the text of the document as a whole, rather than the text inside the cell in isolation Specify KeyBindings from historyKeymap or similar here to enable history shortcuts while inside the cell editorhistoryKeymap defines keyboard shortcuts like Ctrl+Z/Cmd+Z which should undo across the document text rather than just the cell textAnother example is searchKeymap which defines keyboard shortcuts that should search across the entire document text rather than just the cell textConversely, specify KeyBindings from defaultKeymap in extensions instead, since these keyboard shortcuts operate on the CodeMirror editor embedded inside cells and the text inside the cell in isolation |
A theme is a collection of properties that define the CSS color scheme
| Name | Description |
|---|---|
TableTheme.light |
Basic light theme that works well with the CodeMirror default theme |
TableTheme.dark |
Basic dark theme |
TableTheme.githubLight |
Theme based on table colors in GitHub's light theme |
TableTheme.githubDark |
Theme based on table colors in GitHub's dark theme |
TableTheme.githubSoftDark |
Theme based on table colors in GitHub's soft dark theme |
TableTheme.oneDark |
Dark theme that works well with @codemirror/theme-one-dark |
Create custom themes in code or override theme properties in CSS
import { TableTheme } from "codemirror-markdown-tables"
const customDarkTheme = TableTheme.dark.with({
"--tbl-theme-header-row-background": "gray",
"--tbl-theme-outline-color": "green",
}):root {
--tbl-theme-header-row-background: gray;
--tbl-theme-outline-color: green;
}Theme properties correspond directly to CSS variables of the same name defined in :root scope
| Property | Values | Description |
|---|---|---|
--tbl-theme-row-background |
CSS color |
Background color of all cells, unless overriden by other *-row-background properties |
--tbl-theme-header-row-background |
CSS color |
Background color of the cells in the header row |
--tbl-theme-even-row-background |
CSS color |
Background color of the cells in even rows |
--tbl-theme-odd-row-background |
CSS color |
Background color of the cells in odd rows (except the header row) |
--tbl-theme-border-color |
CSS color |
Color of borders |
--tbl-theme-border-hover-color |
CSS color |
Color of a hovered border |
--tbl-theme-border-active-color |
CSS color |
Color of a clicked border |
--tbl-theme-outline-color |
CSS color |
Color of the outline around selected cells |
--tbl-theme-text-color |
CSS color |
Color of text |
--tbl-theme-menu-border-color |
CSS color |
Color of menu borders |
--tbl-theme-menu-background |
CSS color |
Background color of menu items |
--tbl-theme-menu-hover-background |
CSS color |
Background color of a hovered menu item |
--tbl-theme-menu-text-color |
CSS color |
Color of menu item text |
--tbl-theme-menu-hover-text-color |
CSS color |
Color of hovered menu item text |
--tbl-theme-select-all-focus-overlay |
CSS color |
Color of the layer overlaid on table when a Select All takes place and the editor has focus The overlay shows as an alpha layer above the table whereas CodeMirror places its default selection background behind editor text So specify an alpha overlay color that, when mixed with the table background color, mimics the opaque CodeMirror selection background color |
--tbl-theme-select-all-blur-overlay |
CSS color |
Color of the layer overlaid on table when a Select All takes place and the editor doesn't have focus The overlay shows as an alpha layer above the table whereas CodeMirror places its default selection background behind editor text So specify an alpha overlay color that, when mixed with the table background color, mimics the opaque CodeMirror selection background color |
A style is a collection of properties that define fonts and other CSS styles
| Name | Values | Description |
|---|---|---|
TableStyle.default |
{ "--tbl-style-font-family": "system-ui", "--tbl-style-font-size": "inherit", "--tbl-style-menu-font-family": "system-ui", "--tbl-style-menu-font-size": "inherit", "--tbl-style-default-header-alignment": "left", } |
Basic style with sensible defaults |
Create custom styles in code or override style properties in CSS
import { TableStyle } from "codemirror-markdown-tables"
const customStyle = TableStyle.default.with({
"--tbl-style-font-family": '"Comic Sans", sans-serif',
"--tbl-style-font-size": "16px",
}):root {
--tbl-style-font-family: "Comic Sans", sans-serif;
--tbl-style-font-size: 16px;
}Style properties correspond directly to CSS variables of the same name defined in :root scope
| Property | Values | Description |
|---|---|---|
--tbl-style-font-family |
CSS font-family |
Font family of text |
--tbl-style-font-size |
CSS font-size |
Font size of text |
--tbl-style-menu-font-family |
CSS font-family |
Font family of menu item text |
--tbl-style-menu-font-size |
CSS font-size |
Font size of menu item text |
--tbl-style-default-header-alignment |
"left", "center", "right" |
Alignment of text in header cell when its column is otherwise unaligned |
Optional config passed into the markdownTableAutocompleter() function to customize the autocompleter
| Property | Values | Default (when omitted or undefined) | Description |
|---|---|---|---|
options |
{ rows: number, cols: number}[] |
[{ rows: 2, cols: 2 }, { rows: 3, cols: 3 }, { rows: 4, cols: 4 }] |
Options shown in the autocomplete popup |
Optional config passed into the insertEmptyMarkdownTable() function to customize the insert table command
| Property | Values | Default (when omitted or undefined) | Description |
|---|---|---|---|
size |
{ rows: number, cols: number} |
{ rows: 2, cols: 2 } |
Size of the inserted table |
import { basicSetup } from "codemirror"
import { markdown, markdownLanguage } from "@codemirror/lang-markdown"
import { EditorView, keymap } from "@codemirror/view"
import {
insertEmptyMarkdownTable,
markdownTables,
markdownTableAutocompleter,
} from "codemirror-markdown-tables"
const markdownLanguageSupport = markdown({ base: markdownLanguage })
new EditorView({
extensions: [
basicSetup,
markdownLanguageSupport,
markdownLanguageSupport.language.data.of({ autocomplete: markdownTableAutocompleter() }),
markdownTables(),
keymap.of([{ key: "Alt-Mod-t", run: insertEmptyMarkdownTable({ rows: 2, cols: 2 }) }]),
],
parent: document.body,
})import { basicSetup } from "codemirror"
import { defaultKeymap, historyKeymap } from "@codemirror/commands"
import { markdown, markdownLanguage } from "@codemirror/lang-markdown"
import { searchKeymap } from "@codemirror/search"
import { EditorView, highlightSpecialChars, keymap } from "@codemirror/view"
import { Autolink, Emoji, Strikethrough, Subscript, Superscript } from "@lezer/markdown"
import {
insertEmptyMarkdownTable,
markdownTables,
markdownTableAutocompleter,
TableStyle,
TableTheme,
} from "codemirror-markdown-tables"
const markdownLanguageSupport = markdown({ base: markdownLanguage })
new EditorView({
extensions: [
basicSetup,
markdownLanguageSupport,
markdownLanguageSupport.language.data.of({
autocomplete: markdownTableAutocompleter({
options: [
{ rows: 2, cols: 2 },
{ rows: 3, cols: 3 },
],
}),
}),
markdownTables({
theme: {
light: TableTheme.light,
dark: TableTheme.dark.with({
"--tbl-theme-row-background": "#000",
"--tbl-theme-text-color": "#ccc",
"--tbl-theme-menu-background": "#000",
"--tbl-theme-menu-text-color": "#ccc",
}),
},
style: TableStyle.default.with({
"--tbl-style-font-size": "16px",
"--tbl-style-menu-font-size": "14px",
"--tbl-style-default-header-alignment": "center",
}),
markdownConfig: {
extensions: [Strikethrough, Autolink, Subscript, Superscript, Emoji],
},
extensions: [highlightSpecialChars(), keymap.of(defaultKeymap)],
globalKeyBindings: [...historyKeymap, ...searchKeymap],
}),
keymap.of([{ key: "Alt-Mod-t", run: insertEmptyMarkdownTable({ rows: 3, cols: 3 }) }]),
],
parent: document.body,
})/* Only applies in CodeMirror light mode */
:root:has([data-tbl-theme-mode="light"]) {
--tbl-theme-row-background: gray;
}
/* Only applies in CodeMirror dark mode */
:root:has([data-tbl-theme-mode="dark"]) {
--tbl-theme-row-background: #000;
}Copyright © 2026 Chris Kant.
This project is MIT licensed
