Markdown's syntax is easy to learn, and even though the syntax is forgiving, linting can help you avoid unexpected issues.
markdownlint and its CLI tool
markdownlint-cli is the most common tool used for linting Markdown files. As of writing, markdownlint validates Markdown files against a list of 53 rules .
markdownlint-cli can be installed and used locally, but it's also easy to integrate into CI/CD pipelines.
See "Common Markdown Mistakes" for a list of the most common Markdown syntax mistakes I see people make.
Nov 9, 2020 · 4 min read
Markdown provides simple syntax for writing structure documents, but most written markdown would not pass a linter check. Here's a list of 11 common syntax mistakes and how to fix them.
markdownlint-cli has instructions for how to install via
npm and Homebrew , but I'll focus on running it via Docker for OS portability. You can run
markdownlint-cli in a container to lint Markdown files in your current directory like this:
docker run --volume "$PWD:/workdir" \ ghcr.io/igorshubovych/markdownlint-cli:latest \ "**/*.md"
If you want to disable certain markdownlint rules, you can do so like this:
docker run --volume "$PWD:/workdir" \ ghcr.io/igorshubovych/markdownlint-cli:latest \ --disable MD013 MD033 MD041 -- \ "**/*.md"
Note the required
-- which terminates the list of space-separated rule names.
Let's define a markdown file
sample.md with the contents:
#Sample This is a sample Markdown file. It has a number of formatting issues. ### Sub-heading
Here's the example output from
docker run --volume "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md" sample.md:1:1 MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#Sample"] sample.md:1 MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#Sample"] sample.md:4 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2] sample.md:7:1 MD019/no-multiple-space-atx Multiple spaces after hash on atx style heading [Context: "### Sub-heading"]
Adding to GitHub Actions
Linters become a lot more powerful when you add them to your CI pipelines. A linter without enforcement will surely be ignored over time.
Here's a sample of how you could add
markdownlint-cli to a GitHub Actions
.github/workflows/test.yml (feel free to change the filename):
name: Project CI on: pull_request: types: - opened - synchronize # HEAD has changed, e.g. a push happened - reopened jobs: markdown-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: docker run --volume "$PWD:/workdir" ghcr.io/igorshubovych/markdownlint-cli:latest "**/*.md"
Then you can add a branch protection rule to prevent pull request merges without certain passing jobs.