| | |

Prettier 05 ๐ŸŽจ Core Options โ€” printWidth, tabWidth, semi, and singleQuote

โฑ๏ธ Estimated read: 12 min | |

Prettier has a small set of options that adjust its output. Four of them appear in almost every configuration file and account for most of the formatting differences between projects: printWidth, tabWidth, semi, and singleQuote. None of them changes what Prettier can format or how it parses code. They change the shape of the printed resultโ€”where lines break, how deep indentation goes, whether statements end with semicolons, and which quote character wraps strings.

This chapter covers these four options in depth: their defaults, their valid values, how they interact with Prettier’s printing algorithm, and why changing one often produces unexpected changes elsewhere. It also covers the overrides mechanism that lets a single configuration apply different values of these options to different file types, and the limits of what these options can control. By the end, you will understand why printWidth is a guideline rather than a rule, why tabWidth is not the same as “use tabs,” and why semi and singleQuote are the two options teams most often argue about and least often need to.

Key point: These four options control presentation, not behavior. printWidth is a target the printer tries to respect but can exceed when a line cannot be broken. tabWidth sets indentation width, not whether tabs are used. semi and singleQuote toggle two binary choices that Prettier otherwise decides on its own.


Why these four options dominate configuration

The lines-are-long problem. Code lines grow. Function signatures accumulate parameters, object literals accumulate properties, and chained method calls accumulate steps. Without a line-length target, Prettier would print everything on one line or break at arbitrary points. printWidth gives the printer a goal: keep lines under this many characters when possible. The “when possible” qualifier is what makes this option misunderstood. Prettier does not truncate or wrap strings, variable names, or URLs to meet the target. It breaks at syntactic boundaries that exist in the AST, and if no such boundary exists before the target is exceeded, the line exceeds the target .

The indentation-width problem. Different projects use different indentation widths. Two spaces is common in JavaScript and TypeScript; four spaces is common in Python-influenced codebases and some C-family styles. tabWidth tells Prettier how many spaces to use per indentation level when it prints the output. It does not control whether the output uses tabs or spaces. Prettier prints spaces by default. If a project wants tabs, that is a separate decision that Prettier’s printer handles based on the input and the useTabs option, which is outside the scope of these four core options.

The semicolon and quote debate. semi and singleQuote are binary toggles that produce visible, immediate differences in every file. They are also the two options most likely to appear in a team’s style guide and most likely to be debated during onboarding. The Prettier philosophy is that once the team picks a value, the debate ends, because the formatter enforces it without exception. The defaults are semi: true and singleQuote: false, which means Prettier prints semicolons and double quotes unless told otherwise .


a. printWidth: the line-length target

printWidth specifies the line length the printer will try not to exceed. The default is 80 characters. The documentation for Prettier’s options describes it as a “line length that the printer will wrap on” but notes that it is not a hard maximum.

{
  "printWidth": 100
}

The printer measures the intermediate representation of each node and decides whether to break it across lines. For a function call with arguments, it checks whether the entire call fits within printWidth. If it does, the call stays on one line. If it does not, the printer breaks the arguments onto separate lines. For an object literal, the same logic applies: if all properties fit, the object stays compact; if not, each property gets its own line.

The option cannot break constructs that have no break points. A long string literal is printed as-is. A long identifier is printed as-is. A long comment may be reformatted if the parser supports comment wrapping for that language, but the printWidth value alone does not force a break inside a string or a name. This is why the documentation warns that printWidth is a guideline, not a strict limit .

// printWidth: 80
// This line exceeds 80 characters because the string cannot be broken
const message = "This is a very long string literal that will not be wrapped by Prettier";

// This line can be broken at the object boundaries, so it will be
const config = { semi: false, singleQuote: true, printWidth: 100, tabWidth: 2 };
// After Prettier with printWidth 80, the object is broken:
const config = {
  semi: false,
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
};

b. tabWidth: indentation width

tabWidth specifies the number of spaces per indentation level. The default is 2. The option is commonly confused with “use tabs,” which is controlled by a different option (useTabs) that defaults to false. When useTabs is false and tabWidth is 4, Prettier prints four spaces per indentation level. When useTabs is true and tabWidth is 4, Prettier prints one tab per indentation level, and the tab is expected to render as four columns wide in the editor.

{
  "tabWidth": 4
}

Changing tabWidth affects every nested construct: function bodies, object literals, arrays, control flow blocks, and JSX children. It does not affect the line-length target. A line that fit within printWidth at tabWidth: 2 may exceed it at tabWidth: 4 because the indentation consumed more characters. The printer accounts for this when deciding where to break, so increasing tabWidth can cause more lines to break.

c. semi: trailing semicolons

semi controls whether Prettier prints semicolons at the end of statements. The default is true. Setting it to false omits semicolons wherever they are optional, which in JavaScript means almost everywhere except in specific constructs like for loop headers and before lines that begin with (, [, or `, where a leading semicolon is required to avoid parsing ambiguity.

{
  "semi": false
}

Prettier is careful about the cases where omitting a semicolon changes the parse. When semi is false, it will still print a semicolon at the start of a line that would otherwise be interpreted as a continuation of the previous expression. This is why the output is safe to run even with semicolons disabled. The option is not “remove all semicolons”; it is “omit optional semicolons and preserve required ones.”

d. singleQuote: quote style

singleQuote controls whether Prettier uses single quotes or double quotes for string literals. The default is false, which means double quotes. Setting it to true uses single quotes.

{
  "singleQuote": true
}

Prettier does not blindly convert every quote. It counts the quote characters inside the string and chooses the quote style that requires fewer escapes, unless the configuration explicitly overrides that behavior. A string containing an apostrophe will use double quotes even when singleQuote is true, because that avoids escaping the apostrophe. A string containing double quotes will use single quotes even when singleQuote is false. The option sets the preferred style, not an absolute rule that ignores content.


Complete Example Session

// ============================================
// PART 1: DEFAULT CONFIGURATION
// ============================================
// Prettier's defaults for these four options:
{
  "printWidth": 80,
  "tabWidth": 2,
  "semi": true,
  "singleQuote": false
}
// The defaults are conservative and widely compatible.
// ============================================
// PART 2: CHANGING printWidth
// ============================================
{
  "printWidth": 100
}
// Longer lines are tolerated before the printer breaks.
// Fewer object literals and calls will be split across lines.
// ============================================
// PART 3: CHANGING tabWidth
// ============================================
{
  "tabWidth": 4
}
// Four spaces per indentation level instead of two.
// May cause more line breaks because indentation consumes
// more of the printWidth budget.
// ============================================
// PART 4: DISABLING SEMICOLONS
// ============================================
{
  "semi": false
}
// Optional semicolons are omitted.
// Required semicolons (for loop headers, leading semicolons
// before lines starting with ( [ `) are still printed.
// ============================================
// PART 5: USING SINGLE QUOTES
// ============================================
{
  "singleQuote": true
}
// Single quotes are preferred, but strings containing
// apostrophes may still use double quotes to avoid escapes.
// ============================================
// PART 6: COMBINING ALL FOUR
// ============================================
{
  "printWidth": 100,
  "tabWidth": 4,
  "semi": false,
  "singleQuote": true
}
// This is a common team configuration.
// Each option affects a different aspect of the output.
// ============================================
// PART 7: OVERRIDES FOR JSON FILES
// ============================================
{
  "semi": true,
  "singleQuote": true,
  "overrides": [
    {
      "files": "*.json",
      "options": {
        "singleQuote": false
      }
    }
  ]
}
// JSON does not support single-quoted strings.
// The override ensures JSON files use double quotes
// even when JavaScript files use single quotes.
// ============================================
// PART 8: INPUT AND OUTPUT WITH semi: false
// ============================================
// Input (with semicolons):
const x = 1;
const y = 2;

// Output with semi: false:
const x = 1
const y = 2

// But a leading semicolon is preserved when needed:
const a = 1
;[1, 2, 3].forEach(fn)
// The semicolon before the array literal prevents
// it from being parsed as indexing into `1`.
// ============================================
// PART 9: QUOTE SELECTION WITH singleQuote: true
// ============================================
// Input:
const greeting = "Hello";
const apostrophe = "It's fine";
const quotes = 'She said "hello"';

// Output with singleQuote: true:
const greeting = 'Hello'
const apostrophe = "It's fine"  // double quotes kept to avoid escape
const quotes = 'She said "hello"'  // single quotes kept
// ============================================
// PART 10: VERIFYING WHICH OPTIONS APPLY
// ============================================
// Run Prettier with --find-config-path to see the config
// Run Prettier with --check to see if files match the config
//
// To inspect the effective options for a file:
// npx prettier --find-config-path src/app.js
// Then read the file it returns.

The ten parts covered the defaults, each option individually, their combination, overrides for file types, the semicolon-omission behavior, quote selection logic, and verification.


Quick Reference

The four core options

OptionDefaultTypeEffect
printWidth80numberLine length target (guideline)
tabWidth2numberSpaces per indentation level
semitruebooleanPrint trailing semicolons
singleQuotefalsebooleanPrefer single quotes

Interaction effects

ChangeConsequence
Increase printWidthFewer line breaks
Decrease printWidthMore line breaks
Increase tabWidthMore line breaks (indentation consumes budget)
Set semi: falseOptional semicolons omitted; required ones kept
Set singleQuote: trueSingle quotes preferred; escapes avoided

Overrides usage

KeyPurpose
filesGlob(s) matching files for this override
optionsThe four options (and others) to apply
excludeFilesGlob(s) excluding files from this override

Best Practices

โœ… Do This:

// Set printWidth to a value the team agrees on
{ "printWidth": 100 }                                      // โœ…
// Use overrides for file types that need different quote rules
{ "overrides": [{ "files": "*.json", "options": { "singleQuote": false } }] } // โœ…
// Pick semi and singleQuote once and enforce them
{ "semi": false, "singleQuote": true }                     // โœ…
# Verify which config applies before formatting
npx prettier --find-config-path src/app.js                 # โœ…

โŒ Don’t Do This:

// Treat printWidth as a hard maximum
{ "printWidth": 40 }                                       // โŒ (excessive breaking)
// Assume tabWidth controls tabs vs spaces
{ "tabWidth": 4 }                                          // โŒ (does not use tabs)
// Set singleQuote: true and expect every string to use single quotes
{ "singleQuote": true }                                    // โŒ (escapes may override)
// Use different semi values across packages in one repo
// without overrides                                          // โŒ (inconsistent output)

Common Pitfalls

PitfallWhy It HappensFix
Lines still exceed printWidthThe line has no breakable pointAccept it; printWidth is a guideline
Indentation looks wrongtabWidth is not the same as useTabsSet useTabs separately if tabs are desired
Semicolons still appear with semi: falseLeading semicolons are required for parse safetyExpected behavior; do not remove
Strings use double quotes despite singleQuote: trueThe string contains an apostropheExpected; Prettier avoids escapes
Different files format differentlyA nearer config or override appliesCheck --find-config-path for each file

Real-World Examples

1. Default configuration

{ "printWidth": 80, "tabWidth": 2, "semi": true, "singleQuote": false }

2. Common team configuration

{ "printWidth": 100, "tabWidth": 2, "semi": false, "singleQuote": true }

3. Four-space indentation

{ "tabWidth": 4 }

4. No semicolons

{ "semi": false }

5. Single quotes

{ "singleQuote": true }

6. JSON override for double quotes

{ "singleQuote": true, "overrides": [{ "files": "*.json", "options": { "singleQuote": false } }] }

7. Wider lines for TypeScript

{ "printWidth": 120 }

8. Narrow lines for Markdown

{ "overrides": [{ "files": "*.md", "options": { "printWidth": 80 } }] }

9. Verify config path

npx prettier --find-config-path src/index.ts

10. Check formatting against config

npx prettier src/ --check

Visual

printWidth as a guideline

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  printWidth: 80                                              โ”‚
โ”‚                                                              โ”‚
โ”‚  Line: const x = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 };    โ”‚
โ”‚  Length: 52 characters โ”€โ”€โ–บ fits, stays on one line           โ”‚
โ”‚                                                              โ”‚
โ”‚  Line: const x = { alpha: 1, beta: 2, gamma: 3, delta: 4 }; โ”‚
โ”‚  Length: 61 characters โ”€โ”€โ–บ fits, stays on one line           โ”‚
โ”‚                                                              โ”‚
โ”‚  Line: const veryLongIdentifierName = "a string that cannot"โ”‚
โ”‚  Length: 90 characters โ”€โ”€โ–บ exceeds, but string cannot break  โ”‚
โ”‚  Result: printed as-is                                       โ”‚
โ”‚                                                              โ”‚
โ”‚  Line: const x = { aVeryLongPropertyName: 1, another: 2 };  โ”‚
โ”‚  Length: 78 characters โ”€โ”€โ–บ fits (barely)                     โ”‚
โ”‚                                                              โ”‚
โ”‚  Line: const x = { aVeryLongPropertyName: 1, anotherLong: 2 };โ”‚
โ”‚  Length: 82 characters โ”€โ”€โ–บ exceeds, object CAN break          โ”‚
โ”‚  Result: broken across lines                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

tabWidth vs useTabs

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  INDENTATION OPTIONS                                         โ”‚
โ”‚                                                              โ”‚
โ”‚  tabWidth: 2, useTabs: false                                 โ”‚
โ”‚    function foo() {                                          โ”‚
โ”‚      return 1;                                               โ”‚
โ”‚    }                                                         โ”‚
โ”‚    (two spaces per level)                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  tabWidth: 4, useTabs: false                                 โ”‚
โ”‚    function foo() {                                          โ”‚
โ”‚        return 1;                                             โ”‚
โ”‚    }                                                         โ”‚
โ”‚    (four spaces per level)                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  tabWidth: 4, useTabs: true                                  โ”‚
โ”‚    function foo() {                                          โ”‚
โ”‚    โ†’   return 1;                                             โ”‚
โ”‚    }                                                         โ”‚
โ”‚    (one tab per level, rendered as 4 columns)                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

semi: false and leading semicolons

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  semi: false                                                 โ”‚
โ”‚                                                              โ”‚
โ”‚  Input:                                                      โ”‚
โ”‚    const x = 1;                                              โ”‚
โ”‚    const y = 2;                                              โ”‚
โ”‚                                                              โ”‚
โ”‚  Output:                                                     โ”‚
โ”‚    const x = 1                                               โ”‚
โ”‚    const y = 2                                               โ”‚
โ”‚                                                              โ”‚
โ”‚  But:                                                        โ”‚
โ”‚    const a = 1;                                              โ”‚
โ”‚    [1, 2, 3].forEach(fn);                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  Output:                                                     โ”‚
โ”‚    const a = 1                                               โ”‚
โ”‚    ;[1, 2, 3].forEach(fn)                                    โ”‚
โ”‚    โ†‘                                                         โ”‚
โ”‚    Leading semicolon preserved to prevent                    โ”‚
โ”‚    the array from being parsed as a[1]                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

singleQuote and escape avoidance

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  singleQuote: true                                           โ”‚
โ”‚                                                              โ”‚
โ”‚  Input:                                                      โ”‚
โ”‚    const a = "Hello";                                        โ”‚
โ”‚    const b = "It's fine";                                    โ”‚
โ”‚    const c = 'She said "hello"';                             โ”‚
โ”‚                                                              โ”‚
โ”‚  Output:                                                     โ”‚
โ”‚    const a = 'Hello'           (single, no escape)          โ”‚
โ”‚    const b = "It's fine"      (double, avoids escape)       โ”‚
โ”‚    const c = 'She said "hello"' (single, no escape)         โ”‚
โ”‚                                                              โ”‚
โ”‚  Prettier chooses the quote style that requires              โ”‚
โ”‚  fewer escapes, even when singleQuote is true.               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
printWidth default80
printWidth behaviorGuideline, not hard maximum
tabWidth default2
tabWidth behaviorSpaces per indentation level (not tabs)
semi defaulttrue
semi: false behaviorOmit optional semicolons; keep required ones
singleQuote defaultfalse
singleQuote: true behaviorPrefer single quotes; escapes may override
Override mechanismoverrides with files and options

Key takeaways:

  • printWidth is a target, not a limit. Prettier breaks lines at syntactic boundaries and cannot break strings, identifiers, or URLs. Lines will exceed the target when no break point exists.
  • tabWidth controls indentation width, not tab usage. Prettier prints spaces by default. Tab characters require a separate option (useTabs).
  • semi: false does not remove all semicolons. It removes optional ones and preserves the leading semicolons required to prevent parsing ambiguity.
  • singleQuote: true does not force every string to use single quotes. Prettier selects the quote style that avoids escapes, so strings containing apostrophes may still use double quotes.
  • Overrides let one configuration handle multiple file types. A project can use single quotes for JavaScript and double quotes for JSON without separate configuration files.
  • Changing tabWidth can change line breaks. Indentation consumes part of the printWidth budget, so wider indentation can cause more lines to exceed the target and break.

Remember: These four options are presentation decisions, not behavioral ones. They do not change what code does, only how it looks. printWidth and tabWidth shape the geometry of the output; semi and singleQuote toggle two binary choices that Prettier otherwise decides based on context. Set them once, document them in .prettierrc, and stop thinking about them. The purpose of configuration is not to perfect every line; it is to remove the need to discuss formatting at all.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!