# MD013 - Keep lines short for better readability
Aliases: `line-length`
## What this rule does
Checks that lines don't exceed a maximum length to ensure your content is easy to read on all devices.
## Why this matters
- **Improves readability**: Shorter lines are easier to scan and understand quickly
- **Works everywhere**: Content displays properly on mobile devices, terminals, and narrow windows
- **Better for code reviews**: Side-by-side comparisons work better with reasonable line lengths
- **Accessibility**: Screen readers and assistive technologies handle shorter lines more effectively
## Examples
<!-- rumdl-disable MD013 -->
### ✅ Correct
```markdown
This line is a reasonable length that's easy to read
and displays well on all devices.
Even with links, you can keep lines manageable by using
[reference-style links][1] instead of inline URLs.
[1]: https://example.com/very-long-url-that-would-make-the-line-too-long
```
### ❌ Incorrect
```markdown
This is an extremely long line that goes on and on and makes it difficult to read the content, especially on mobile devices or when viewing files in split-screen editors or during code reviews where horizontal space is limited.
```
### 🔧 Fixed
```markdown
This is a line that has been wrapped to stay within
the maximum length, making it much easier to read
and work with in various contexts.
```
<!-- rumdl-enable MD013 -->
## Configuration
```toml
[MD013]
line-length = 100 # Maximum characters per line (default: 80)
code-blocks = false # Don't check code blocks (default: true)
code-spans = false # Don't flag lines whose only overflow is an unbreakable inline code span (default: true)
tables = false # Don't check tables (default: false)
headings = true # Check headings (default: true)
paragraphs = true # Check paragraph/regular text (default: true)
blockquotes = true # Check blockquote content (default: true)
strict = false # Disables exceptions for URLs, etc. (default: false)
stern = false # Like strict, but unwrappable single-token lines are still permitted (default: false)
ignore-link-urls = true # Ignore inline link/image URLs when measuring line length (default: true)
heading-line-length = 100 # Optional per-context limit for headings; falls back to line-length when unset
code-block-line-length = 120 # Optional per-context limit for code blocks; falls back to line-length when unset
reflow = false # Enable automatic text reflow/wrapping (default: false)
reflow-mode = "default" # Reflow mode: "default", "normalize", "sentence-per-line", or "semantic-line-breaks" (default: "default")
length-mode = "visual" # How to count line length: "visual", "chars", or "bytes" (default: "visual")
abbreviations = ["Assn", "Univ"] # Add custom abbreviations for sentence-per-line mode
require-sentence-capital = true # Require uppercase after periods for sentence detection (default: true)
atomic-spans = true # Treat code/emphasis spans as atomic units during reflow (default: true)
```
### Configuration options explained
- `line-length`: The maximum number of characters allowed per line (set to `0` to disable all line length checks)
- `code-blocks`: Whether to check line length in code blocks (default: `true`)
- `code-spans`: Whether to check lines whose length comes from an inline code span (default: `true`). Inline code spans (`` `like this` ``) cannot be wrapped, so reflow cannot shorten a line whose excess length is one. When `false`, a line is not reported if it would fit within the limit once its inline code spans are excluded - useful with `reflow` so an unbreakable code incantation does not fail an otherwise-clean file
- `tables`: Whether to check line length in tables (default: `false`)
- `headings`: Whether to check line length in headings (default: `true`)
- `paragraphs`: Whether to check line length in regular text/paragraphs (default: `true`). When false, `line-length` is still used for reflow but no warnings are reported. Blockquote content is treated as paragraph text, so `paragraphs = false` also skips blockquotes
- `blockquotes`: Whether to check line length in blockquote content (default: `true`). Set to `false` to skip blockquote lines specifically without disabling all paragraph checks
- `strict`: When true, disables exceptions for URLs and other special content (default: `false`). Overrides `stern`.
- `stern`: Tighter than the default but laxer than strict. Disables the trailing-token forgiveness used in default mode (so a line is flagged even when only the final token spills past the limit), but keeps the "unwrappable line" exemption — lines that consist of a single non-whitespace token, optionally prefixed by `#` heading or `>` blockquote markers, are still permitted (default: `false`). Mirrors markdownlint's `stern` option.
- `ignore-link-urls`: Whether to forgive a line that exceeds the limit only because of the URL inside an inline `[text](url)` / `` (default: `true`; see [Ignoring inline link URLs](#ignoring-inline-link-urls-non-strict-mode)). Set to `false` to count those URLs toward the line length. Combined with `stern`, this flags a link line that has wrappable text around it while still exempting a line that is a single unbreakable token (a bare URL or a standalone link): stricter than `stern` about links, but without `strict` flagging genuinely unbreakable link lines. Has no effect under `strict` (which already disables all forgiveness). The former key `semantic-link-understanding` is still accepted as an alias.
- `heading-line-length`: Per-context maximum length for heading lines. Unset (`null`) falls back to `line-length`; `0` means "no limit for headings". Mirrors markdownlint's `heading_line_length` option.
- `code-block-line-length`: Per-context maximum length for fenced or indented code-block lines. Unset (`null`) falls back to `line-length`; `0` means "no limit for code blocks". Mirrors markdownlint's `code_block_line_length` option.
- `reflow`: When true, enables automatic text reflow to wrap long lines intelligently (default: `false`)
- `reflow-mode`: Controls how text is reflowed when `reflow` is true (default: `"default"`, see Reflow Modes section below)
- `length-mode`: How to calculate line length (default: `"visual"`):
- `"visual"`: Count visual display width (emoji = 2 columns, CJK = 2 columns).
**Recommended and default**. Correctly handles international content and matches terminal display.
- `"chars"`: Count Unicode characters (emoji = 1, CJK = 1). Use only for backward compatibility.
- `"bytes"`: Count raw UTF-8 bytes (not recommended for Unicode text).
- `abbreviations`: Custom abbreviations for sentence-per-line mode (optional)
- Periods are optional: both `"Dr"` and `"Dr."` work the same
- Added to built-in defaults: `Mr`, `Mrs`, `Ms`, `Dr`, `Prof`, `Sr`, `Jr`, `i.e`, `e.g`, `vs`, `fig`, `no`, `vol`, `ch`, `sec`, `al`
- `require-sentence-capital`: Whether to require uppercase after periods for sentence boundary detection (default: `true`)
- When `true`, only `word. Capital` is treated as a sentence boundary (fewer false positives)
- When `false`, `word. lowercase` is also treated as a sentence boundary (more splitting)
- Does not affect `!` and `?` which are always treated as sentence boundaries
- `atomic-spans`: Whether to hold emphasis/strong/strikethrough and code spans atomic during reflow (default: `true`). When `true`, these spans are treated as atomic units. When `false`, they can be wrapped word-by-word like normal text. Note that if an emphasis span contains a nested code span, the emphasis span is kept atomic to prevent formatting corruption.
## Ignoring inline link URLs (non-strict mode)
In non-strict mode, this rule understands that inline links and images often contain
long URLs that the author cannot reasonably shorten. When a line exceeds the limit,
the rule checks whether replacing each `[text](url)` with just `[text]` (and
`` with `![alt]`) would bring the line within the limit. If so, the
warning is suppressed.
This applies to:
- Inline links: `[text](url)` and `[text](url "title")`
- Inline images: ``
- Nested badge patterns: `[](link-url)`
This does **not** apply to:
- Reference links (`[text][ref]`) — these already have no inline URL
- Autolinks (`<url>`) — the URL itself is the visible content
- Strict mode — all exceptions are disabled
- `ignore-link-urls = false`: count inline link/image URLs toward the line length. With `stern`, this flags a link line that has wrappable text around it, while a line that is a single unbreakable token (a bare URL or a standalone link) is still permitted by stern's unwrappable-line exemption.
### Example
With `line-length = 80`:
```markdown
<!-- No warning: text without URLs is only ~30 chars -->
See the [installation guide](https://example.com/docs/getting-started/installation/v2) for details.
<!-- Warning: text alone still exceeds 80 chars -->
This is already a very long sentence with lots of words that pushes past the limit even [without](https://example.com) the URL.
```
## Automatic fixes
When `reflow` is set to `true`, this rule can automatically wrap long lines while preserving Markdown formatting:
- Intelligently breaks lines at appropriate points
- Preserves bold, italic, links, code spans, and other Markdown elements
- Reflows list items using the configured marker spacing and aligns continuation lines to the content column (see [List reflow and marker spacing](#list-reflow-and-marker-spacing))
- Reflows blockquote paragraphs (including lazy continuation lines) while preserving input style
- Preserves hard line breaks (two trailing spaces)
- Does not wrap code blocks, tables, headings, or reference definitions
### List reflow and marker spacing
When reflowing list items (including nested and blockquote-nested lists), rumdl
does not hard-code the spacing after a list marker. In the spacing-normalizing
modes (`default` and `normalize`) it derives the number of spaces after the
marker from the [MD030](md030.md) configuration — `ul-single`/`ul-multi` for
unordered lists, `ol-single`/`ol-multi` for ordered lists, and the
`ol-align-column` override — choosing the single- or multi-line value the same
way MD030 does. Continuation (wrapped) lines are then indented to the resulting
content column, so wrapped text aligns under the item's text exactly as
[MD007](md007.md)'s text-aligned style expects. With the default MD030 setting
(a single space) the output is unchanged.
The `sentence-per-line` and `semantic-line-breaks` modes only change where lines
break, so they preserve the marker spacing and indentation already in the
source. The MkDocs flavor keeps its own fixed 4-space structural indent.
Example (`line-length = 50`, MD030 `ol-multi = 2`):
```markdown
# Before
1. This is a fairly long numbered item that wraps onto another line.
# After
1. This is a fairly long numbered item that wraps
onto another line.
```
### Blockquote reflow style preservation
When reflowing blockquote paragraphs, rumdl preserves the source style:
- Explicit continuation input stays explicit (`> ` on wrapped lines)
- Lazy continuation input stays lazy when safe
- Mixed explicit/lazy input uses deterministic style selection (explicit on ties)
- Lazy output is automatically upgraded to explicit for lines that would otherwise start a new block structure
Example (`line-length = 60`):
```markdown
# Before
> This is a very long blockquote line that should be wrapped while preserving the source style and markdown structure.
# After
> This is a very long blockquote line that should be wrapped
> while preserving the source style and markdown structure.
```
### Reflow Modes
The `reflow-mode` option controls how text is reformatted when `reflow` is true:
#### `default` mode
Standard text wrapping that breaks lines at word boundaries to fit within the configured line length.
```toml
[MD013]
line-length = 80
reflow = true
reflow-mode = "default"
```
#### `normalize` mode
Normalizes all paragraph text to consistently wrap at the configured line length, removing irregular line breaks.
```toml
[MD013]
line-length = 80
reflow = true
reflow-mode = "normalize"
```
This mode is useful for standardizing documents with inconsistent line wrapping.
#### `sentence-per-line` mode
Enforces one sentence per line, making diffs cleaner and easier to review, among [other advantages](https://nick.groenen.me/notes/one-sentence-per-line/). This mode:
- Detects sentence boundaries (periods, exclamation marks, question marks)
- Handles common abbreviations (e.g., i.e., Mr., Dr., Ph.D., Inc., etc.) without breaking sentences
- Preserves decimal numbers and ellipses
- Works with markdown formatting
```toml
[MD013]
line-length = 80
reflow = true
reflow-mode = "sentence-per-line"
```
Example transformation:
```markdown
# Before
This is the first sentence. This is the second sentence. And this is the third.
# After
This is the first sentence.
This is the second sentence.
And this is the third.
```
This mode is particularly useful for:
- Technical documentation where each sentence often contains a single concept
- Documents maintained in version control where sentence-level diffs are clearer
- Collaborative writing where different authors work on different sentences
**Safety exception:** a sentence is kept on the previous line when placing it
on its own line would change the document's structure. Text that starts with a
list marker (`- `, `* `, `+ `, `1. `), blockquote marker (`>`), heading marker
(`# `), code fence, horizontal rule, reference definition (`[label]:`), or
block-level HTML tag becomes real block syntax at the start of a line, so
reflow never puts it there:
```markdown
# Stays on one line (the dash clause would otherwise become a list item)
Google Calendar (Can't we get rid of this dependency? - I don't really see the need)
```
Structure always wins over strict one-sentence-per-line in these cases. The
same guard applies to line wrapping in all reflow modes, where a wrapped line
is instead broken one word earlier (or left slightly over the limit when no
earlier break point exists).
#### `semantic-line-breaks` mode
Breaks lines at semantic boundaries using a cascading strategy. Unlike `sentence-per-line` (which always breaks at every sentence boundary only), this mode also splits long sentences at clause punctuation and break-words to keep lines within the configured line length.
**Cascading priority:**
| Priority | Split point | When applied |
|----------|-------------|--------------|
| 1 | Sentence boundaries (`.` `!` `?`) | Always |
| 2 | Clause punctuation (`,` `;` `:` `—`) | When line > line-length |
| 3 | Break-words (`and`, `or`, `but`, `which`, `because`, ...) | When line still > line-length |
| 4 | Word wrap | Fallback |
```toml
[MD013]
line-length = 80
reflow = true
reflow-mode = "semantic-line-breaks"
```
Example transformation:
```markdown
# Before
All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.
# After (with line-length = 80)
All human beings are born free and equal in dignity and rights.
They are endowed with reason and conscience
and should act towards one another in a spirit of brotherhood.
```
**Break-words list:** `and`, `or`, `but`, `nor`, `yet`, `so`, `for`, `which`, `that`, `because`, `when`, `if`, `while`, `where`, `although`, `though`, `unless`, `since`, `after`, `before`, `until`, `as`, `once`, `whether`, `however`, `therefore`, `moreover`, `furthermore`, `nevertheless`, `whereas`
With `line-length = 0`, only sentence boundaries are used (no cascading), behaving like `sentence-per-line`.
#### Sentence-per-line without line length warnings
If you want to use sentence-per-line mode for formatting but don't want warnings about long sentences, you can disable paragraph checking while keeping code blocks and tables checked:
```toml
[MD013]
line-length = 80
paragraphs = false # Don't warn about long paragraphs
code-blocks = true # Still check code blocks
tables = true # Still check tables
reflow = true
reflow-mode = "sentence-per-line"
```
This configuration is useful when:
- You want automatic sentence-per-line formatting without validation noise
- You care about line length in code blocks and tables but not in regular text
- You're using semantic line breaks where sentence length is determined by content, not arbitrary limits
#### Disabling all line length checks
If you want to completely disable all line length checking (for paragraphs, headings, code blocks, and tables), set `line-length` to `0`:
```toml
[MD013]
line-length = 0 # Disable all line length checks
reflow = true
reflow-mode = "sentence-per-line"
```
When `line-length` is set to `0`, no line length warnings will be reported for any content type. This is particularly useful for:
- Projects using semantic line breaks where lines break at logical boundaries regardless of length
- Sentence-per-line workflows where line length is not a concern
- Documentation where long lines (e.g., URLs, technical terms) are unavoidable and acceptable
**Note**: With `line-length: 0` and `reflow-mode: "sentence-per-line"`, rumdl will:
- Split multiple sentences that share a line into separate lines
- Join single sentences that span multiple lines into one line (since there's no length constraint)
- Provide consistent "one sentence = one line" formatting throughout your document
### Example with automatic reflow
```toml
[MD013]
line-length = 80
reflow = true
```
With this configuration, long lines will be automatically wrapped to fit within 80 characters while maintaining proper Markdown formatting.
**Note**: When `reflow` is `false` (default), automatic fixes are not available and you'll need to manually wrap long lines.
## Learn more
- [Line length best practices](https://en.wikipedia.org/wiki/Line_length)
- [Readability and line length](https://baymard.com/blog/line-length-readability)
## Related rules
- [MD009](md009.md): Remove trailing spaces at line ends
- [MD010](md010.md): Use spaces instead of tabs
- [MD047](md047.md): End files with a single newline