Skip to content

EditorConfig Deep Dive: Syntax, Properties, and Best Practices

EditorConfig logo

Every shared repository eventually rediscovers the same avoidable argument: tabs or spaces, LF or CRLF, trim trailing whitespace or leave it alone, final newline or not. None of these questions are hard. They are just repetitive, noisy, and surprisingly good at wasting review time.

EditorConfig exists to move those decisions out of personal editor settings and into the repository itself. It is a simple, cross-editor standard for defining basic text file formatting rules so contributors using different editors and IDEs still save files in a consistent way.

This matters more than style bikeshedding. Inconsistent indentation creates ugly diffs. Inconsistent line endings break shell scripts and CI jobs. Trailing whitespace produces review noise that hides the change you actually care about. EditorConfig is not glamorous, but it solves a class of problems that should stay boring.

This guide covers how EditorConfig works, the syntax of .editorconfig files, the standard properties you can rely on, where editor support stands, and how to build a practical starter template for a modern repository.

What EditorConfig Is

At its core, EditorConfig is two things:

  1. A file format for declaring editor behavior.
  2. A set of editor integrations that read those declarations and apply them when a file is opened or saved.

The file is always named .editorconfig. You commit it to the repository, usually at the root. Editors that support EditorConfig then use it to answer questions like:

  • Should this file use tabs or spaces?
  • If spaces, how many?
  • What line ending should be written on save?
  • Should trailing whitespace be removed?
  • Should the file end with a newline?

EditorConfig is intentionally narrow. It does not replace a formatter like Prettier, Black, or gofmt, and it does not replace a linter like ESLint, Ruff, or ShellCheck. It handles the foundational text-formatting rules that are editor-shaped rather than language-shaped.

Why It Exists

The problem is not that teams disagree. Teams always disagree. The problem is letting those disagreements stay implicit.

Without EditorConfig, each contributor's editor falls back to local defaults. That creates several predictable failure modes:

Problem What It Looks Like In Practice
Tabs vs. spaces drift Nested code lines up differently depending on who saved the file
Mixed line endings A one-line change becomes a full-file diff because LF became CRLF
Trailing whitespace noise Pull requests show meaningless changes on otherwise untouched lines
Missing final newline Some tools complain, and diffs show \ No newline at end of file
Editor-specific defaults A repo feels tidy in one IDE and messy everywhere else

EditorConfig gives the repository a say. That is the real win.

How EditorConfig Works

EditorConfig resolution is straightforward once you know the search model.

The .editorconfig File

An EditorConfig file is plain text, stored as .editorconfig, and written in an INI-like format. It contains:

  • An optional preamble at the top
  • One or more sections in square brackets
  • Key/value pairs inside those sections

The most important preamble setting is:

root = true

That tells EditorConfig-aware tools to stop searching parent directories for additional .editorconfig files.

Hierarchical Lookup

When you open a file, the EditorConfig integration starts in that file's directory and walks upward through parent directories looking for .editorconfig files.

The search stops when one of these happens:

  • It reaches the filesystem root
  • It finds a .editorconfig file whose preamble contains root = true

This lets large repositories define broad defaults at the top level and narrower overrides in subdirectories.

For example:

repo/
├── .editorconfig
├── src/
│   └── app.ts
└── docs/
    ├── .editorconfig
    └── guide.md

In that structure:

  • src/app.ts is affected by repo/.editorconfig
  • docs/guide.md is affected by both files
  • The closer file wins when both define the same property

Order of Precedence

EditorConfig processing follows two simple rules:

  1. Files closer to the target file win over files higher in the tree.
  2. Within a single .editorconfig file, later matching sections win over earlier ones.

That means EditorConfig is both hierarchical and top-down:

  • The tool reads matching files from the top of the directory tree down to the closest one
  • Inside each file, it reads from top to bottom
  • The most recently applied matching property wins

This makes broad defaults easy:

[*]
end_of_line = lf
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

Here, Markdown files still inherit lf and insert_final_newline, but they override trailing-whitespace trimming.

Syntax and File Matching Deep Dive

EditorConfig looks simple because it is simple, but there are a few details worth getting right.

File Format Basics

The format is INI-like:

  • Blank lines are ignored
  • Comment lines start with # or ;
  • Section names go in square brackets
  • Properties use key = value

Example:

root = true

[*]
indent_style = space
indent_size = 2

[*.py]
indent_size = 4

A Current Spec Detail: No Inline Comments

Modern EditorConfig specification behavior does not treat # or ; as inline comment markers when they appear later in a line. They only start comments when they are the first non-whitespace character on the line.

In other words, this is safe:

foo = value # this is part of the value in the modern spec

Because older parsers historically handled inline comments differently, the most portable habit is simple: put comments on their own lines.

Section Matching Rules

Each section header is a glob pattern that matches file paths. The pattern applies relative to the directory containing that .editorconfig file.

If the pattern contains no slash, it can match files at any depth below that directory. If it contains a slash, it becomes path-aware.

Examples:

[*.js]
[src/*.ts]
[docs/**/*.md]

Wildcards and Glob Patterns Reference

These are the standard special patterns defined by the EditorConfig specification.

Pattern Meaning Example
* Match any string except / [*.js] matches app.js
** Match any string including / [docs/**/*.md] matches nested Markdown files
? Match any single character except / [file?.txt] matches file1.txt
[seq] Match one character from a set [file[ab].txt] matches filea.txt or fileb.txt
[!seq] Match one character not in a set [!T]*.md excludes names starting with T
{s1,s2,s3} Match one of several strings [*.{js,ts,jsx,tsx}]
{num1..num2} Match an integer range [file{1..3}.txt] matches file1.txt through file3.txt

Practical Matching Examples

Pattern Matches Notes
[*] All files Common default section
[*.py] Any Python file below this directory No slash, so any depth
[Makefile] Files literally named Makefile Case-sensitive in the pattern
[docs/*.md] Markdown files directly under docs/ Not recursive
[docs/**/*.md] Markdown files at any depth under docs/ Recursive
[*.{json,yml,yaml}] Multiple extensions Cleaner than repeating sections
[{package.json,tsconfig.json}] Specific filenames Useful for indentation overrides

Removing a Previously Applied Value

Any property can be set to unset to remove its effect and fall back to the editor default or a lower-priority rule.

Example:

[*]
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[docs/generated/*.md]
trim_trailing_whitespace = unset

unset is especially useful in large repositories with nested overrides.

Core Properties Reference

These are the properties you can count on across mainstream EditorConfig implementations. They are the heart of the format.

Property Allowed Values What It Controls
indent_style space, tab Whether indentation uses spaces or hard tabs
indent_size Integer, tab Width of one indentation level
tab_width Integer Display width of a hard tab
end_of_line lf, cr, crlf Line ending format written on save
charset latin1, utf-8, utf-8-bom, utf-16be, utf-16le Character encoding
trim_trailing_whitespace true, false Whether spaces before newline are removed
insert_final_newline true, false Whether a file should end with a newline

Indentation Style

indent_style controls whether indentation uses tabs or spaces.

indent_style = space

Allowed values:

  • space
  • tab

For most repositories, spaces are the safer shared default. If you do use tabs, define tab_width clearly so editors display them consistently.

Indentation Size

indent_size controls the number of columns in one indentation level.

indent_size = 4

Allowed values:

  • Any whole number, such as 2, 4, or 8
  • tab

When set to tab, the indent size follows tab_width if tab_width is specified. This is useful when you want hard tabs but still want consistent visual alignment.

Tab Width:

tab_width controls how wide a tab character should be displayed.

tab_width = 4

Allowed values:

  • Any whole number

You usually do not need this if indent_style = space. It matters when tabs are part of the repository's indentation rules or when file types require hard tabs, such as Makefiles.

End of Line

end_of_line controls how line endings are written:

end_of_line = lf

Allowed values:

  • lf
  • cr
  • crlf

This matters on cross-platform teams because operating systems have different historical defaults:

Value Meaning Common Use
lf Line Feed (\n) Linux, macOS, containers, CI
crlf Carriage Return + Line Feed (\r\n) Windows-native tooling
cr Carriage Return (\r) Legacy systems only

For modern repositories, lf is the usual default. It keeps shell scripts, Docker builds, and Unix-like CI runners happy. If you need Windows-specific scripts to use CRLF, override them by file type.

Also remember the boundary: EditorConfig controls how editors save files. .gitattributes controls how Git normalizes files in the repository. For line endings, the two complement each other rather than compete. If you want a deeper Git-side treatment, see Controlling Git Repository Behavior with .gitattributes.

Character Encoding

charset controls the text encoding used when saving.

charset = utf-8

Allowed values:

  • latin1
  • utf-8
  • utf-8-bom
  • utf-16be
  • utf-16le

For almost every modern codebase, utf-8 is the right answer. utf-8-bom is still supported by the specification but generally best avoided unless a specific tool requires it.

Trim Trailing Whitespace

trim_trailing_whitespace controls whether whitespace at the end of lines is removed on save.

trim_trailing_whitespace = true

Allowed values:

  • true
  • false

This setting is almost always worth enabling globally. The common exception is Markdown, where two trailing spaces can be used intentionally to force a hard line break.

Final Newline Insertion

insert_final_newline controls whether the file should end with a newline.

insert_final_newline = true

Allowed values:

  • true
  • false

This is a small setting with outsized value. It keeps diffs cleaner, satisfies Unix tooling expectations, and avoids the classic \ No newline at end of file annoyance.

Editor and IDE Support

EditorConfig support is broad, but it is not identical everywhere. Some editors include it natively, while others still rely on an extension or plugin.

As of April 29, 2026.

The lists below reflect the support information published on editorconfig.org as checked on April 29, 2026. That landscape can change, so treat this as a dated snapshot rather than eternal truth.

Common Editors with Native Support

According to the EditorConfig project site, these popular tools support EditorConfig without an extra plugin:

Editor or Platform Notes
Visual Studio Professional Native support
JetBrains IntelliJ IDEA Listed as supported on the official site
JetBrains WebStorm Native support
JetBrains PyCharm Native support
JetBrains RubyMine Native support
JetBrains Rider Native support
PHPStorm Native support
Vim Native support
Emacs Native support
Helix Native support
Xcode Native support
GitHub Recognizes EditorConfig in supported editing contexts
GitLab Listed as supported on the official site
Gitea Listed as supported on the official site

Common Editors That Require a Plugin or Extension

The official EditorConfig site lists these popular tools under plugin-based support:

Editor Typical Integration Path
Visual Studio Code Install the EditorConfig extension
Sublime Text Install the EditorConfig package
Eclipse Install the EditorConfig plugin
Atom Install the EditorConfig package
Notepad++ Install the plugin
NetBeans Install the plugin
TextMate Install the plugin
VSCodium Install an EditorConfig extension

If your team standardizes on VS Code, it is worth calling this out in onboarding docs. A repository-level .editorconfig only helps if contributors actually have EditorConfig support enabled in the editor they use.

For related editor setup ideas, see Inside My VS Code Setup: Theme, Extensions, and Settings.

Pros and Cons

EditorConfig is worth using in almost every repository, but it helps to understand what it does well and what it does not try to do.

Pros

  • Language-agnostic. The same file can define sane defaults for Python, Go, Markdown, JavaScript, shell scripts, and plain text.
  • Stops basic style arguments early. Teams do not need a meeting to settle every editor default.
  • Lightweight. The format is tiny, readable, and easy to review.
  • Version-controlled. Rules live with the repository, which makes onboarding easier and changes auditable.
  • Complements formatters and linters. It handles the low-level editor behavior that language-specific tools often assume.
  • Useful even in mixed-tool shops. JetBrains, Visual Studio, Vim, Emacs, web editors, and other tools can still converge on the same basics.

Cons

  • Limited scope. EditorConfig is not a formatter, linter, or code style engine. It will not sort imports, wrap lines intelligently, or enforce language-specific conventions.
  • Support varies by editor. Some tools need plugins, and teams forget plugins more often than they forget to argue about tabs.
  • Not every property is honored equally everywhere. The standard is clear, but editor integrations can differ in edge cases.
  • It solves only the first layer of consistency. You still need tools like Prettier, ESLint, Black, Ruff, gofmt, or pre-commit for deeper enforcement.

If you want a practical way to enforce these rules before code reaches CI, pair EditorConfig with pre-commit.

Practical Example: The Ultimate Starter Template

The best .editorconfig is not the most clever one. It is the one your team understands at a glance and rarely needs to revisit.

This template is a strong starting point for a modern repository with JavaScript, TypeScript, Python, Markdown, YAML, JSON, shell scripts, and Makefiles.

.editorconfig
# Stop searching above the repository root.
root = true

# Default rules for all files.
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

# JavaScript and TypeScript usually use 2 spaces.
[*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}]
indent_style = space
indent_size = 2

# JSON, YAML, and web config files are commonly 2 spaces.
[*.{json,jsonc,yml,yaml,html,css,scss}]
indent_style = space
indent_size = 2

# Python convention is 4 spaces.
[*.py]
indent_style = space
indent_size = 4

# Shell scripts are usually 2 or 4 spaces. Pick one and be explicit.
[*.{sh,bash,zsh}]
indent_style = space
indent_size = 2

# Go uses tabs for indentation in source files.
[*.go]
indent_style = tab
tab_width = 4

# Markdown often needs trailing spaces for intentional hard line breaks.
[*.md]
trim_trailing_whitespace = false

# Makefiles require real tab characters for recipe lines.
[Makefile]
indent_style = tab
tab_width = 4

# Windows command scripts may need CRLF for compatibility.
[*.{bat,cmd,ps1}]
end_of_line = crlf

# Generated or vendored content is sometimes best left alone.
[vendor/**]
indent_size = unset
trim_trailing_whitespace = unset
end_of_line = unset

Why This Template Works

It follows a few sane defaults:

  • Global rules are simple and conservative.
  • Language-specific overrides are explicit.
  • Markdown gets a trailing-whitespace exception.
  • Makefiles and Go get tab-aware handling.
  • Windows-native scripts can opt into CRLF without polluting everything else.

If your repository uses Prettier, Black, or another formatter, keep this file focused on editor behavior. Let the formatter own formatter-shaped decisions.

A Simpler Template for Small Repositories

If your repository is mostly prose, shell, and a little code, you may only need this:

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab

Use the boring tool when boring is the point.

Best Practices

EditorConfig pays off fastest when the file stays small, obvious, and intentional.

1. Start with One Root File

Most repositories should begin with a single root-level .editorconfig and only add subdirectory overrides when there is a real need.

Too many nested files make rule resolution harder to reason about.

2. Keep the Defaults Broad and the Overrides Specific

This pattern scales well:

  • Put shared rules in [*]
  • Override only the file types that genuinely differ
  • Avoid repeating the same property in many sections

3. Prefer utf-8 and lf Unless a Tool Requires Otherwise

These are the least surprising defaults for modern development across macOS, Linux, CI runners, containers, and Git-hosted repositories.

4. Make Markdown an Explicit Exception

If your team writes documentation, blog posts, or release notes in Markdown, consider:

[*.md]
trim_trailing_whitespace = false

That avoids breaking intentional hard line breaks.

5. Pair It with .gitattributes

EditorConfig tells editors how to save files. .gitattributes tells Git how to normalize and treat files in the repository. If line endings matter to your workflow, use both.

6. Pair It with pre-commit or CI Checks

EditorConfig is strongest when local editor behavior and automation reinforce each other. At minimum, consider pre-commit hooks or CI checks for:

  • Trailing whitespace
  • Final newline
  • Mixed line endings
  • Formatter drift

7. Document the Expectation

Especially for VS Code or other plugin-based editors, tell contributors that the repository uses EditorConfig and mention any required extension in your CONTRIBUTING.md or onboarding docs.


If your repository does not already have a .editorconfig, add one. Keep it short. Start with UTF-8, LF, final newline insertion, trailing whitespace trimming, and an explicit indentation rule. Then add exceptions only where the file types justify them.

EditorConfig will not end every code-style debate on its own, and it is not meant to. What it does is remove the low-value arguments and a surprising amount of diff noise before they ever reach review. That is enough to make it one of the highest-leverage tiny files in a repository.

References