Prettier 3.7: Enhanced Formatting Consistency and New Plugin Features

Developer Tools

Prettier 3.7 delivers significant improvements in formatting consistency for TypeScript and Flow, resolves numerous bugs, adds support for Angular 21 and GraphQL 16.12, and introduces powerful new APIs for plugin developers.

We are thrilled to announce the release of Prettier 3.7! This update primarily focuses on refining the TypeScript and Flow formatting experience, introducing greater consistency and predictability for classes and interfaces. We also invite your feedback on an upcoming change regarding inconsistent opening brace logic for class and interface bodies.

Beyond these core improvements, Prettier 3.7 includes fixes for numerous bugs, adds support for new features in Angular 21 and GraphQL 16.12, and brings Front Matter support to Handlebars. For plugin developers, new APIs are available to offer enhanced control over comment attachment and handling of ignored nodes.

If you value Prettier's work, please consider sponsoring us directly via our OpenCollective or by sponsoring the projects we depend on. Your continued support is greatly appreciated!

Author: Fisker Cheung

Highlights

TypeScript

Fix inconsistent printing between class and interface (#18094, #18091, #18215 by @fisker)

This release significantly enhances the consistency between Class and Interface formatting. Previously, these similar constructs could be formatted quite differently, leading to visual inconsistencies. We've aligned their formatting rules for a cleaner and more predictable output. This includes:

  • Removal of extra indentation for type parameters in classes. For example:
    // Prettier 3.6
    interface MarkDef<
      M extends string | Mark = Mark,
      ES extends ExprRef | SignalRef = ExprRef | SignalRef,
    > extends A, B {
    }
    declare class MarkDef<
      M extends string | Mark = Mark,
      ES extends ExprRef | SignalRef = ExprRef | SignalRef,
    > implements A, B {
    }
    
    // Prettier 3.7
    interface MarkDef<
      M extends string | Mark = Mark,
      ES extends ExprRef | SignalRef = ExprRef | SignalRef,
    > extends A, B {
    }
    declare class MarkDef<
      M extends string | Mark = Mark,
      ES extends ExprRef | SignalRef = ExprRef | SignalRef,
    > implements A, B {
    }
    
  • Alignment of interface heritage printing with classes.
  • Alignment of single heritage printing with super classes.

Inconsistent opening brace print logic of class and interface body

In Prettier v2.3, the opening brace { of a class body was moved to a new line when the class had multiple heritages, aiming to improve visual separation. This change, however, was not universally welcomed. We are now seeking community input on applying this same change to interfaces by leaving a comment on this issue. If no better solution emerges, this alignment for interface bodies will be implemented in Prettier v4.

// Prettier 3.7
declare class loooooooooooooooooooong implements looooooooooooooooooong, loooooooooooooooooooong {
  property: string;
}
interface loooooooooooooooooooong extends looooooooooooooooooong, loooooooooooooooooooong {
  // <-- This
  property: string;
}

These changes also impact Flow syntax.

Other Changes

JavaScript

  • Allow import attributes to break into multiple lines (#17329 by @fisker)
    // Prettier 3.6
    import syntaxImportAssertions from "@babel/plugin-syntax-import-assertions" with {
      BABEL_8_BREAKING: "false",
      USE_ESM: "true",
      IS_STANDALONE: "false"
    };
    
    // Prettier 3.7
    import syntaxImportAssertions from "@babel/plugin-syntax-import-assertions" with {
      BABEL_8_BREAKING: "false",
      USE_ESM: "true",
      IS_STANDALONE: "false",
    };
    
  • Add support for "Discard Bindings" proposal (#17708 by @fisker) Prettier now supports the Stage 2 "Discard Bindings" proposal via Babel.
    const [void] = x;
    const { x: void } = x;
    
  • Fix inconsistent comment format (#17723 by @fisker) Comments within complex if conditions now maintain their intended position.
    // Prettier 3.6 (--parser=babel --experimental-operator-position=start)
    if (
      true
      // This is a really complicated part of the condition, so we need a big ol'
      // comment here to explain it.
      && flibble.blibble.blobble?.bloo
    ) {
      doThings();
    }
    
    // Prettier 3.7
    if (
      true
      // This is a really complicated part of the condition, so we need a big ol'
      // comment here to explain it.
      && flibble.blibble.blobble?.bloo
    ) {
      doThings();
    }
    
  • Add additional Playwright test functions (#17876 by @BPScott) Prettier now avoids changing indentation for test.fixme, test.describe.skip, and test.describe.fixme functions, similar to test.skip.
  • Avoid breaking {import,require.resolve,require.resolve.paths,import.meta.resolve}() with long module names (#17882, #17908 by @kovsu & @fisker)
    // Prettier 3.6
    const b = require.resolve(
      "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module",
    );
    
    // Prettier 3.7
    const b = require.resolve(
      "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"
    );
    
  • Improve comment handling inside if statement (#17998 by @fisker)
    // Prettier 3.6
    if (foo) {
      // comment
      doThing();
    }
    // comment for else
    else {
      doSomethingElse();
    }
    
    // Prettier 3.7
    if (foo)
    // comment
    {
      doThing();
    }
    else
    // comment for else
    {
      doSomethingElse();
    }
    
  • Improve require() call with comments (#18037 by @fisker)
    // Prettier 3.6
    require(
      // Comment
      "foo"
    );
    
    // Prettier 3.7
    require(
      // Comment
      "foo",
    );
    
  • Remove indentation in logical expression in Boolean() call (#18087 by @kovsu) Reduces diffs when changing conditions or converting between !! and Boolean().
    // Prettier 3.7
    const foo = Boolean(
      a_long_long_condition ||
      a_long_long_long_condition ||
      a_long_long_long_condition
    );
    
  • Fix comments handling for for-statements (#18099 by @fisker, @sosukesuzuki)
    // Prettier 3.6
    // Comment
    for (x of y) bar();
    
    // Prettier 3.7
    for (x of y)
    // Comment
    bar();
    
  • Improve comment printing around empty statement (#18108 by @fisker)
    // Prettier 3.7
    for (
      index = 0;
      doSomething(foo[index]) !== bar && doSomething(foo[index]) !== baz;
      index++
    )
    /* No op */
    ;
    
  • Fix inconsistent comment print between class methods and object methods (#18147 by @fisker)
    // Prettier 3.6
    class x {
      method() {
        // Class method
        return 1;
      }
    }
    const object = {
      method() {
        // Object method
        return 1;
      },
    };
    
    // Prettier 3.7
    class x {
      method() {
        // class method
        return 1;
      }
    }
    const object = {
      method() {
        // object method
        return 1;
      },
    };
    
  • Add missing parentheses in bitwise operators (#18163 by @fs0414) Ensures correct operator precedence.
    // Prettier 3.6
    1 << bit % 8;
    
    // Prettier 3.7
    1 << (bit % 8);
    
  • Fix inconsistent break for array literals (#18172 by @Dunqing, @fisker)
    // Prettier 3.7
    assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides___(-1, -1), [1],);
    assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(-1, -1), [1, 2],);
    
  • Fix inconsistent logical expression print (#18205 by @fisker)
    // Prettier 3.7
    fn(
      a &&
      a_long_long_long_long_long_long_long_long_long_long_long_long_long_condition
        ? a
        : b,
    );
    
  • Fix inconsistent print between CallExpression and NewExpression (#18206 by @fisker)
    // Prettier 3.7
    TelemetryTrustedValue(
      instance.capabilities.get(TerminalCapability?.PromptTypeDetection)?.promptType,
    );
    new TelemetryTrustedValue(
      instance.capabilities.get(TerminalCapability?.PromptTypeDetection)?.promptType,
    );
    
  • Remove redundant parentheses around JSX element (#18243 by @fisker)
    // Prettier 3.6
    new A(
      (
        <div>
          <div></div>
        </div>
      ),
    );
    
    // Prettier 3.7
    new A(
      <div>
        <div></div>
      </div>,
    );
    
  • Improve formatting of logical expression as callee of new expression (#18245 by @fisker)
    // Prettier 3.7
    a = new (
      a_long_long_long_long_condition ||
      a_long_long_long_long_condition ||
      a_long_long_long_long_condition
    )();
    
  • Remove empty line in for statement without "update" (#18300 by @fisker)
    // Prettier 3.7
    for (
      let i = 0, j = 0, len = allMatches.length, lenJ = selections.length;
      i < len;
    ) {
    }
    
  • Improve super class format (#18325 by @fisker)
    // Prettier 3.7
    class EnsureNoDisposablesAreLeakedInTestSuiteSuite extends eslint.Rule.RuleModule {}
    

TypeScript (Continued)

  • Fix misplacement of comments after arrow (#17421 by @o-m12a, @t-mangoe)
    // Prettier 3.7
    export const test = (): any =>
      /* first line
      second line
      */
      null;
    
  • Add missing parentheses to arrow function in instantiation expression (#17724 by @fisker)
    // Prettier 3.7
    void (<_T extends never>() => {})();
    
  • Fix TSMappedType format (#17785 by @fisker) Resolves a crash when formatting specific mapped types.
    // Prettier 3.7
    export type A = B extends { C?: { [D in infer E]?: F } } ? G : H;
    
  • Print trailing comma in TSImportType options (#17798 by @fisker) Supports trailing commas in import type attributes as fixed in TypeScript v5.9.
    // Prettier 3.7
    type A = import("foo", {
      with: {
        type: "json",
      },
    });
    
  • Improve CommonJS module require() with comments (#18035 by @fisker)
    // Prettier 3.7
    import foo = require(
      // Comment
      "foo"
    );
    
  • Line breaking after = in type parameters (#18043 by @fisker)
    // Prettier 3.7
    export type OuterType2<
      LongerLongerLongerLongerInnerType = LongerLongerLongerLongerLongerLongerLongerLongerOtherType,
    > = {
      a: 1
    };
    
  • Remove unexpected blank line before union types (#18109 by @jspereiramoura, @fisker)
    // Prettier 3.7
    type SuperLongTypeNameLoremIpsumLoremIpsumBlaBlaBlaBlaBlaBlaBlaBlaBlaBlaBlaBla =
      | Fooo1000
      | Baz2000
      | BarLoooooooooooooooooooooooooooooooooooooooooooooooooLong;
    
    This change also affects Flow syntax.
  • Fix inconsistent comment printing between typescript and flow parser (#18110 by @fisker) Ensures consistent comment placement across both parsers for interface types.
    // Prettier 3.7 (Same output for `--parser=typescript` and `--parser=flow`)
    interface A {
      a: B;
      // Comment
      b:
      // Comment
      | Fooo1000
      | Baz2000
      | BarLoooooooooooooooooooooooooooooooooooooooooooooooooLong;
      c:
      // Comment
      Fooo1000
      & Baz2000
      & BarLoooooooooooooooooooooooooooooooooooooooooooooooooLong;
    }
    
  • Fix missing semicolon before call signatures (#18118 by @fisker)
    // Prettier 3.7
    interface A {
      foo;
      <T>(): T
    }
    
  • Fix inconsistent print of as const between flow and typescript parsers (#18161 by @fisker) Standardizes comment placement with as const.
    // Prettier 3.7 (Same output for `--parser=typescript` and `--parser=flow`)
    1 /* comment */ as const;
    
  • Fix comment around as / satisfies expression (#18162 by @fisker)
    // Prettier 3.7
    1 as const /*
    comment
    */;
    
  • Fix misaligned union type (#18165 by @fisker)
    // Prettier 3.7
    interface I {
      elements:
      // comment
      | [string, ExpressionNode, ExpressionNode]
      | [string, ExpressionNode, ExpressionNode, ObjectExpression];
    }
    
  • Remove extra empty line in union type (#18197 by @Dunqing)
    // Prettier 3.7
    type SuperLongTypeNameLoremIpsumLoremIpsumBlaBlaBlaBlaBlaBlaBlaBlaBlaBlaBlaBla =
      | Fooo1000
      | Baz2000
      | BarLoooooooooooooooooooooooooooooooooooooooooooooooooLong;
    

Flow

  • Add basic support for "match" syntax (#17681 by @fisker) Prettier now supports Flow's match syntax.
    // Prettier 3.7
    const e = match(a) {
      1 => true,
      "foo" => false,
      2 => {
        obj: "literal"
      },
    };
    
  • Add support for opaque type with lower and upper bound (#17792 by @SamChou19815)
    // Prettier 3.7
    

opaque type Counter super empty extends Box = Container; opaque type Counter super Box = Container; ```

CSS

  • Handle attribute selector with case-sensitive and uppercase case-insensitive flags (#17841, #17865 by @kovsu)
    /* Prettier 3.7 */
    [type="a" s],
    [type="a" S],
    [type="a" I] {
      list-style-type: lower-alpha;
    }
    
  • Fix crash when formatting special custom properties (#17899 by @fisker) Resolves a crash when encountering custom properties like --l: , #000;.
  • Fix selector being lowercased incorrectly inside css modules (#17929 by @kovsu) Preserves original casing for selectors in CSS modules, e.g., myColor.
  • Fix formatting of CSS selectors containing // (#17938 by @kovsu)
    /* Prettier 3.7 */
    a[href="http://example.com"] {
      color: red;
    }
    
  • Remove unexpected space between font size and line height (#18114 by @kovsu)
    /* Prettier 3.7 */
    a {
      font: var(--size)/1;
    }
    
  • Fix extra indent for CSS comma-separated values after a block comment (#18228 by @seiyab)
    /* Prettier 3.7 */
    .foo {
      background-image:
        linear-gradient(to top, blue, red 100%),
        /* texture */
        repeating-linear-gradient(90deg, pink, yellow 20px, green 20px, pink 40px),
        repeating-linear-gradient(90deg, pink, yellow 20px, green 20px, pink 40px);
    }
    

SCSS

  • Fix parentheses space problem in mixin argument (#17836 by @kovsu)
    // Prettier 3.7
    .foo {
      @include bar('A (B)');
    }
    
  • Fix formatting of space-separated values (#17903 by @kovsu)
    // Prettier 3.7
    .foo {
      @include transition(min-height ($spacer / 2) ease-in-out);
    }
    

Less

  • Fix variable name being lowercased incorrectly (#17820 by @kovsu) Preserves original casing for Less variable names.
    // Prettier 3.7
    @fooBackground: line-gradient(#f00);
    a {
      background: @fooBackground;
    }
    
  • Keep property/variable accessors tight (#17983 by @kovsu)
    // Prettier 3.7
    .average(@x, @y) {
      @result: ((@x + @y) / 2);
    }
    div {
      padding: .average(16px, 50px)[@result];
    }
    

HTML

  • Support formatting allow attribute of iframe element (#17879 by @kovsu)
    <!-- Prettier 3.7 -->
    <iframe
      allow="
        layout-animations 'none';
        unoptimized-images 'none';
        oversized-images 'none';
        sync-script 'none';
        sync-xhr 'none';
        unsized-media 'none';
      "
    ></iframe>
    
  • Format inline event handler (#17909 by @kovsu) Prettier now formats JavaScript within inline event handlers.
    <!-- Prettier 3.7 -->
    <button type="button" onclick="console.log('Hello, this is my old-fashioned event handler!')">
      Press me
    </button>
    

Angular

  • Support Angular 21 (#17722, #18294 by @fisker) Adds support for new assignment operators introduced in Angular 20.1 and regular expressions in Angular 21.
    <!-- Prettier 3.7 -->
    <b (click)="a ??= b">
      {{ /\d+/g }}
    </b>
    
  • Fix comments getting duplicated in interpolation (#17862 by @fisker)
    <!-- Prettier 3.7 -->
    {{
      a() // comment
    }}
    
  • Fix formatting of "non-null assertion" (#18047 by @fisker)
    <!-- Prettier 3.7 -->
    {{ foo?.bar!.baz }}
    

Ember / Handlebars

  • Added Front Matter support to Handlebars (#17781 by @Codezilluh) Handlebars files can now include Front Matter.
    ---
    

title: My page title keywords:

  • word
  • other word

{{title}}

    {{#each keywords}}
  • {{this}}
  • {{/each}}
``` * **Preserve `else if` syntax for custom helpers** ([#17856](https://github.com/prettier/prettier/pull/17856) by @kovsu) ```handlebars {{! Prettier 3.7 }} {{#animated-if this.foo}} foo content {{else if (this.bar)}} bar content {{/animated-if}} ``` * **Remove extra blank lines in ` ``` * **Don't force elements to break with `--html-whitespace-sensitivity=ignore`** ([#18133](https://github.com/prettier/prettier/pull/18133) by @fisker) Ensures elements remain on a single line when `html-whitespace-sensitivity` is set to `ignore`.

GraphQL

  • Support "executable descriptions" (#18214 by @fisker) Prettier now supports descriptions on executable definitions.
    # Prettier 3.7
    "Description"
    query {
      node {
        id
      }
    }
    

Markdown

  • Improve emoji size measurement (#17813 by @seiyab) Enhances table alignment in Markdown, especially with emojis.
    <!-- Prettier 3.7 -->
    |     |     |
    | :-: | :-: |
    |  ✔  |  ✘  |
    |  ✘  |  ✔  |
    |  ✔  |  ✘  |
    
  • Infer TOML parser for Front Matter (#17965 by @kovsu) TOML Front Matter is now processed by the appropriate plugin, if available, for HTML and CSS files as well.
  • Fix strong emphasis format (#18010 by @yin1999) A supplementary fix for #17143, ensuring correct handling of nested strong/italic emphasis.
    <!-- Prettier 3.7 -->
    1 ** * 2 * ** 3
    1 ** * 2 * ** 3
    

MDX

  • Fix import and export parsing (#17996 by @kovsu & @fisker) Ensures import and export keywords are correctly parsed when appearing within Markdown lists in MDX files.
    {/* Prettier 3.7 */}
    - import is a word in lists
    - export is a word in lists, too!
    

YAML

  • Preserve empty line between map and comment (#17843 by @kovsu)
    # Prettier 3.7
    only: issues
    
    # Comment
    
  • Preserve explicit document end marker (#18296 by @fisker)
    # Prettier 3.7
    a: a
    ---
    b: b
    ...
    c: c
    ...
    ---
    d: d
    
  • Use explicit key style for flow-mapping keys with trailing comments (#18324 by @kovsu, @fisker)
    # Prettier 3.7
    {
      ?"foo" # comment
      : bar
    }
    

API

  • Allow plugin.parser.preprocess() to return a Promise (#17679 by @fisker) Aligns with plugin.printer.preprocess(), which already supports Promises. While supported, async work is still recommended for plugin.parser.parse().
  • Allow AstPath#call() to access property of nullish properties (#17860 by @fisker) AstPath#call() no longer throws errors when attempting to access properties of potentially null or undefined child nodes, simplifying checks for non-existent nodes.
  • Pass ancestors to plugin.printer.canAttachComment() (#18055 by @fisker) Plugins can now use ancestor information to prevent comments from being attached to non-printable child nodes, avoiding comment loss.
    export const canAttachComment = (node, [parent]) =>
      !(
        parent?.type === "Property" &&
        parent.shorthand &&
        parent.key === node &&
        parent.key !== parent.value
      );
    
  • Add support for plugin.printer.printPrettierIgnored() (#18070 by @fisker) Plugins can now customize how prettier-ignored nodes are printed, allowing for custom parenthesization or leading semicolons to prevent ASI issues in --no-semi mode.
  • Allow plugin to provide an estree printer (#18072 by @fisker) Plugins can now create a plugin that solely provides an estree printer, simplifying development for those building upon built-in estree formatting.

CLI

  • Avoid creating node_modules/.cache/ directory when --cache is not enabled (#18124 by @chiawendt) Running prettier . without the --cache flag no longer creates an empty node_modules/.cache/ directory, improving cleanliness.