---
title: "Markdown tabs vs spaces: how a tab counts"
slug: markdown-tabs-vs-spaces
excerpt: "How CommonMark counts a tab in indented code, list items and block quotes, tested with pandoc on files holding real tab bytes."
quickAnswer: "Markdown tabs vs spaces comes down to one short section of the CommonMark spec about tab stops. This page puts that section in plain words and tests it with pandoc on indented code, list items, numbered items and block quotes, using files that hold real tab bytes. It also looks at a tab between two words, and the section on testing a file with tabs gives a short checklist for your own file."
keyTakeaways:
  - "The section on what the spec says puts the tab rule in plain words and explains what a tab stop is."
  - "Indented code has a section of its own, with tested examples and a note on a pandoc option."
  - "List items and numbered items each get worked cases that show where a tab puts the text."
  - "Block quotes get two tested cases: one tab and two tabs."
  - "The section on testing a file with tabs has a numbered checklist for checking a suspect file with pandoc."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: Can one tab replace four spaces in Markdown?
    a: "The section on what the spec says has the tab-stop explanation and examples."
  - q: Why did my tab-indented line turn into a code block?
    a: "The list item and block quote sections show the tab counts that lead to a code block."
  - q: Why does pandoc print spaces where my tabs were?
    a: "The indented code section covers pandoc's default and the option that changes it."
related:
  - markdown-code-blocks
  - markdown-numbered-lists
  - markdown-block-quotes-nested-lists
---

You indented a line with a tab, and the rendered page put it somewhere you didn't plan. A second paragraph turned into a code block, or a line meant for a quote showed up as code.

A tab in Markdown isn't a fixed four spaces. What it adds depends on where it starts on the line. You'll get the CommonMark rule behind markdown tabs vs spaces, a pandoc test of each case, and a note on where one tab and four spaces give the same result and where they don't.

## How the examples are written

This post writes each tab byte as `→`. Read each `→` as one tab. If you copy an example into a test file, type a real tab there.

Every result quoted below came from pandoc 3.8.3, run as `pandoc -f commonmark -t html test.md` on a file holding real tab bytes, not arrows.

## What the spec says

CommonMark 0.31.2 has a short section called "Tabs." It says tabs in a line are not expanded to spaces. Where spaces help define block structure, though, a tab behaves as if it were replaced by spaces with a tab stop of 4 characters.

A tab stop of 4 means a tab doesn't add four spaces each time. It moves the position forward to the next multiple of 4, counted from the beginning of the line. So the count depends on what sits before it:

- A tab at the very start of a line counts as four spaces.
- Two spaces and then a tab still reach position 4. The tab counts as two.
- A `>` and then a tab: the `>` uses one position, so the tab counts as three. The spec spells this case out.

The same section adds that tabs which aren't doing structural work, such as a tab between two words in a code block, pass through as literal tabs.

## Indented code

The spec's own first example is a tab in place of four spaces at the start of an indented code block. Here's a test file:

```markdown
→Jane→Doe→→Holdings
```

The opening tab reaches position 4. Pandoc returns a `<pre><code>` element holding `Jane`, `Doe` and `Holdings`. The three tabs between the words aren't structure, so the spec keeps them as tabs in the code. Why that indent makes code is explained by the [code blocks post](markdown-code-blocks.md).

Pandoc's default differs on that last point. Its manual says it converts tabs to spaces before parsing, and that the `--preserve-tabs` option keeps them in code blocks and code spans. The test bears this out. With `pandoc -f commonmark -t html test.md`, spaces sit between the words. With `pandoc -f commonmark -t html --preserve-tabs test.md`, the tab bytes come through.

Starting from two spaces works too:

```markdown
  →Jane Doe
```

The tab moves from position 2 to position 4, so pandoc still gives a code block containing `Jane Doe`.

You can also mix the two inside one block. A first line indented with four spaces and a second line indented with one tab both count as four, and pandoc puts them in a single code block. For fences, info strings and other code block rules, see [fenced and indented code](markdown-code-blocks.md).

## List items

For indenting a later paragraph under a list item, see the indenting section of the [numbered list post](markdown-numbered-lists.md) and the [nesting guide](markdown-block-quotes-nested-lists.md). This section covers how a tab counts toward that indent.

```markdown
- Jane Doe

→Doe Holdings
```

The item's text starts at position 2. The tab on the last line reaches position 4, which is far enough. Pandoc puts `Doe Holdings` in the same `<li>` as a second `<p>`. The spec says this has exactly the same effect as four spaces, and a test file using four spaces gave identical output.

Now add a second tab:

```markdown
- Jane Doe

→→Doe Holdings
```

Two tabs reach position 8, six past where the text starts. For this input, pandoc's output was a `<pre><code>` element in the `<li>`, with two spaces before `Doe Holdings`. That matches the spec's example for this case. For why an indent inside an item becomes code, open the [fenced and indented code](markdown-code-blocks.md) post.

A tab right after the marker is fine on its own. For `-→Jane Doe`, the tab moves from position 1 to position 4, and pandoc returns a plain list item with the text `Jane Doe`.

### Numbered items

The tab stop gives a numbered list an odd property. Compare these two files:

```markdown
1.→Jane Doe

→Doe Holdings
```

```markdown
10.→Jane Doe

→Doe Holdings
```

After `1.` the tab fills two positions. After `10.` it fills one. Both land at position 4, so the item text starts at the same place in each. A continuation line that opens with one tab also reaches position 4, and pandoc places `Doe Holdings` in the item as a second paragraph in both files. With spaces, the gap after the marker is whatever you typed, so you'd count it by hand.

## Block quotes

The [nesting guide](markdown-block-quotes-nested-lists.md) covers the block quote marker itself. When a tab follows a `>` that opens the line, the tab counts as three spaces.

```markdown
>→Jane Doe
```

Pandoc reads this as a `<blockquote>` holding an ordinary `<p>` with `Jane Doe`.

```markdown
>→→Jane Doe
```

The second tab adds four more. For this input, pandoc gave a `<pre><code>` element in the `<blockquote>`, with two spaces before `Jane Doe`, which is the result the spec gives for this example. For quotes holding lists and lists holding quotes, open the [nesting guide](markdown-block-quotes-nested-lists.md).

## A tab between two words

Outside block structure, the spec doesn't expand tabs. In a paragraph, a tab is just a character. For `Jane→Doe` in a plain paragraph, pandoc returned `<p>Jane Doe</p>` with a single space, and adding `--preserve-tabs` did not change that. Pandoc's manual says tabs in regular text are always treated as spaces. Other viewers may show the gap differently, so don't use a mid-line tab to line things up. Columns belong in a [table](markdown-tables.md).

## Testing a file with tabs

When a page renders a block in a place you didn't plan, test the lines in isolation.

1. Copy the lines around the problem into a scratch file, such as `test.md`.
2. Run `pandoc -f commonmark -t html test.md` and read the HTML.
3. Check for `<pre><code>` where you expected `<p>`, or a `<p>` that sits outside the `<li>` or `<blockquote>` you meant it for.
4. For each tab that starts a line or follows a marker, work out the position it reaches using the tab stop of 4.
5. Replace those tabs with the matching number of spaces, run pandoc again, and compare.

With spaces, the parser counts each space character in the file as one position. A tab's share depends on its starting position, so one tab can count as four, three, two or one. If you keep tabs, keep them for lines where you've checked the count. If the file comes from an agent, say which you want in the instructions you give it. Our post on [phrasing instructions for an agent](write-markdown-docs-your-ai-agent-can-follow.md) helps with that kind of rule.

## Where Sashmark fits

Planned as a desktop tool, Sashmark is meant for checking Markdown that your agents write. It is in development and has not been released. On the plan for v1: code blocks, lists and block quotes drawn as formatted text, with edits made straight on that view and untouched lines saved exactly as they were. The app itself has no AI features. If an agent replaces the file's text, the plan is for the page to reload and hold your place, with a Changes View to mark what's different since your last read. The [roadmap](/roadmap) shows status, the [help pages](/help) carry how-to notes, and [planned features](/features) shows the scope.

Watch [sashmark.com](https://www.sashmark.com/) for news.
