Markdown tables, code blocks, and Mermaid diagrams: a practical guide
Write markdown tables, code blocks, and Mermaid diagrams that render right the first time, with copy-paste syntax, alignment rules, and pitfall fixes
Markdown tables are built from pipes (|) and a divider row of dashes; the divider also sets column alignment. Code blocks are wrapped in triple backticks, with an optional language name for syntax highlighting. Mermaid diagrams are written as plain text inside a mermaid code block and rendered into a flowchart, sequence diagram, or similar. All three are plain text, so they live as text in any .md file.
This guide shows the raw syntax for each, the result you should expect, and the mistakes that trip people up. Everything here is standard GitHub Flavored Markdown (GFM), with Mermaid as a widely supported add-on. If your editor renders these, you can write a table or a diagram without leaving the keyboard.
How do you write a markdown table?
A table needs three parts: a header row, a divider row of dashes, and one or more data rows. Columns are separated by pipes.
Here is the raw Markdown:
| Tool | Storage | Price |
| --------- | --------------- | ------------ |
| Noteline | Plain .md files | One-time |
| Notebook | Proprietary | Subscription |
Here is what it renders to:
| Tool | Storage | Price |
|---|---|---|
| Noteline | Plain .md files | One-time |
| Notebook | Proprietary | Subscription |
A few rules worth knowing:
- The divider row is required. Without it, the lines are just text with pipes in them.
- Each divider cell needs at least one dash (
---). The exact number does not matter. - You do not have to line the pipes up. Extra spaces are ignored, so
|Tool|Storage|works the same as the padded version. Padding is for your eyes, not the parser. - The leading and trailing pipes are optional in GFM, but keeping them makes columns easier to scan in raw text.
That last point is the advantage of a table written in plain text: the source is legible before anything renders it, and it diffs cleanly in version control.
Why markdown tables beat spreadsheet screenshots
A common habit is to drop a screenshot of a spreadsheet into a note. It looks fine until the data changes. Then you re-open the spreadsheet, edit a cell, re-export the image, and replace it. A markdown table skips all of that.
| Approach | Editable in place | Searchable | Diffs in Git |
|---|---|---|---|
| Spreadsheet screenshot | No | No | No (binary) |
| Markdown table | Yes | Yes | Yes |
Because the table is text, you can grep it, fix a typo without a round trip through another app, and see exactly which cell changed in a commit. The trade-off is real: a markdown table has no formulas and no charts. For a budget that does live math, a spreadsheet still wins. For a comparison, a status list, or a small reference table inside a note, markdown is faster and more durable. The same logic applies to whole documents, which is part of why people moving off database-style apps like Notion end up back in plain text for the parts that need to last.
How do you align table columns?
Alignment lives in the divider row. A colon on the left, right, or both sides tells the renderer where to push the text.
| Left | Center | Right |
| :-------- | :-----: | ----: |
| apples | apples | 1.20 |
| oranges | oranges | 14.00 |
:---means left-aligned.:--:means centered.---:means right-aligned.---with no colon uses the renderer's default, which is usually left.
Right-aligning a column of numbers makes them easier to scan, because every value's trailing character lines up against the same edge. Note what this does not do: Markdown has no decimal-point alignment. Right alignment lines up the last character, not the period. If your values have a different number of fractional digits, 1.2 and 14.005 will not have their points in a column. The fix is to pad the numbers yourself so every value has the same number of digits after the point (here, 1.20 and 14.00 both have two). Once they match, right alignment and decimal alignment look the same, but it is the padding doing the work, not the syntax.
What about cell content and line breaks?
Tables are deliberately simple. A few things you can't do:
- Pipes inside a cell must be escaped, or the parser reads them as a new column. This is what bites anyone who puts a shell command with a pipe into a table.
- No real line breaks. A cell is one line. If you need a break inside a cell, some renderers accept a literal
<br>tag, but plain newlines will not work. - Inline formatting works. You can use
**bold**,`code`, and[links](https://example.com)inside a cell. - Empty cells are fine. Leave the space between pipes blank.
If you find yourself fighting the table to fit multi-paragraph content, that is a sign the data wants to be a list or a set of headings instead.
How do fenced code blocks work?
A fenced code block is three backticks on their own line, your code, then three backticks to close. Add a language name right after the opening fence and most renderers will apply syntax highlighting.
Here is the raw Markdown for a code block, shown by wrapping it in a longer fence of four backticks:
```js
function greet(name) {
return `Hello, ${name}`;
}
```
The js after the opening backticks is the language hint. It does not change the code; it only tells the highlighter which grammar to use. Common hints include js, ts, python, bash, json, yaml, sql, html, and css. Leave it off and you get a plain monospace block with no coloring, which is fine for output or logs.
Two details worth knowing:
- To show backticks inside a code block, wrap the whole thing in a longer fence. If your content contains a triple backtick, open and close with four backticks, as above. The outer fence just needs to be longer than anything inside it.
- Indentation is preserved exactly. Code blocks are verbatim, so tabs and spaces come through untouched. That is the point.
For short snippets inside a sentence, use a single backtick pair instead: write `git status` to get git status. Inline code does not highlight, but it keeps technical terms from being mangled by autocorrect or italic conversion.
What is a Mermaid diagram and how do you start one?
Mermaid turns text into diagrams. You write the diagram as code inside a fenced block tagged mermaid, and a renderer that supports it draws the picture. A renderer without Mermaid support draws nothing; it shows the raw fenced code block, so a reader sees the diagram source as text rather than a broken image. The text stays readable everywhere, even where the drawn picture isn't.
A flowchart is the usual starting point. The first word sets the direction: TD is top-down, LR is left-to-right.
```mermaid
flowchart TD
A[Write note] --> B{Has table?}
B -->|Yes| C[Add pipes and divider]
B -->|No| D[Keep typing]
C --> E[Preview renders it]
D --> E
```
Reading the syntax:
flowchart TDdeclares the type and direction.A[Write note]is a node. Square brackets give it a rectangular box; curly braces likeB{Has table?}make a decision diamond.-->is an arrow between nodes.-->|Yes|puts a label on the arrow.- Node IDs (
A,B,C) are how you connect things; the text in brackets is what shows on screen.
How do you write a Mermaid sequence diagram?
Sequence diagrams show messages passing between participants over time. They are useful for sketching an API call or a sign-in flow.
```mermaid
sequenceDiagram
participant U as User
participant A as App
U->>A: Open file
A-->>U: Show live preview
U->>A: Edit table
A-->>U: Re-render
```
sequenceDiagramsets the type.participant U as Usernames a column and gives it a short alias.->>is a solid-line message;-->>is a dashed reply.- Lines run top to bottom in the order you write them.
Mermaid covers more diagram types (class, state, Gantt, pie), but flowcharts and sequence diagrams handle most note-taking needs. Start there and add others when you have a reason.
What are the most common pitfalls?
Here are the errors that most often render wrong:
| Pitfall | Symptom | Fix |
|---|---|---|
| Missing divider row | Table shows as plain text with pipes | Add a divider row of dashes under the header |
| Unescaped pipe in a cell | Extra empty column appears | Escape it as a backslash-pipe (|) for a literal pipe |
| Fence too short | Backticks inside a block break it early | Use a longer outer fence (four or more) |
| Wrong language hint | No highlighting, or odd colors | Use a known name like js or python, or omit it |
| Mermaid typo in a node | Whole diagram fails to render | Check brackets and arrow syntax line by line |
| Blank line inside a table | Table splits into two | Keep all rows contiguous, no empty lines |
One more habit: Markdown is whitespace-sensitive in specific spots. A blank line ends a table or a list. A code fence inside a list must be indented to align with the list item's content, and some lightweight renderers handle that nesting poorly even though GFM and CommonMark support it. When something refuses to render, look at the lines just above and below it first.
Why this matters for your notes
A table organizes anything with rows and columns. A code block keeps a command or config exact, down to the whitespace. A Mermaid diagram replaces a screenshot of a flowchart with text you can edit and diff. Because all of it is plain text, your notes stay greppable, version-controllable, and readable in any editor years from now. That is the case for plain Markdown files and for future-proofing your notes.
The catch with this syntax is the gap between writing it and seeing it. Counting backticks and eyeballing whether a table is well-formed is easy to get wrong. A live-preview editor closes that gap by rendering as you type. Noteline works this way: open a .md file, and bold, headings, tables, code blocks, and Mermaid diagrams render inline while the file on disk stays plain text. You can try it in the free web editor without installing anything.