What's New in QML formatting

The latest updates to qmlformat, Qt's own tool for formatting of QML files, bring two practical improvements: configurable line breaking and automatic import sorting. These features enhance code readability and maintainability, making them valuable additions worth highlighting.

Line Breaking After Specified Length

Long lines of code can be difficult to read and navigate, particularly when working with complex QML components that have numerous properties.

As of Qt 6.9, qmlformat can automatically break lines that exceed a specified character length.

Option Description
-W, --column-width < width > Breaks lines that exceed the specified width. Use -1 to disable line wrapping (default).

Line Breaking in Action

You can specify a maximum line length, and qmlformat will intelligently break lines that exceed this limit. For example:

// main.qml
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control
highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled }

Using qmlformat without line breaking options makes this less readable:

import QtQuick
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
id: control
highlighted: control.pressed || control.hovered || (control.ListView.view && control.ListView.view.currentIndex === index && control.ListView.view.highlightFollowsCurrentItem && control.enabled
}

Using qmlformat -W 80 main.qml produces a more readable result:


import QtQuick
import QtQuick.Controls.Material
import QtQuick.Templates as T

T.ItemDelegate {
            id: control
            highlighted: control.pressed || control.hovered || (
                                     control.ListView.view
                                     && control.ListView.view.currentIndex
                                     === index
                                     && control.ListView.view.highlightFollowsCurrentItem
                                     && control.enabled

Sorting Import Statements

Managing cluttered or inconsistently ordered import statements can be challenging and often leads to unnecessary merge conflicts when multiple contributors modify the same file.

To address these issues, Qt 6.10 introduces the --sort-imports option in qmlformat. Sorted imports improve readability by clearly showing which modules are used, reduce merge conflicts by minimizing unnecessary diffs, and enforce a consistent structure across all QML files.

Option Description
-S, --sort-imports Sort imports alphabetically (Warning: this might change semantics if a given name identifies types in multiple modules!)

 

⚠️ Warning: Sorting imports alphabetically with --sort-imports can change the semantics of your QML files if the same type name exists in multiple modules. Always review the results to ensure your application behaves as expected.

Sort Imports in Action


// Original main.qml
import QtQuick.Templates
import MyCustomModule

Button {
}

After running qmlformat -S main.qml:

import MyCustomModule
import QtQuick.Templates

Button {
}

In this example, the original main.qml uses MyCustomModule’s Button. Formatting with --sort-imports changes the semantics to use the non-visual Button from QtQuick.Templates.

Upcoming: Customizable Semicolon Rule

As of Qt 6.10, we’ve introduced a new –semicolon-rule option, giving users more control over how JavaScript semicolons are handled during formatting. The new flag supports two modes: always, which appends semicolons to all JS statements, and essential, which removes them unless omitting them would lead to ambiguity due to JavaScript’s automatic semicolon insertion (ASI) rules.

Mode Description
always Appends semicolons to all JavaScript statements. (default)
essential Removes semicolons unless ASI (Automatic Semicolon Insertion) makes it unsafe.

 

Note: This option only affects semicolons at the end of JavaScript statements. Semicolons at the end of QML elements are always removed by qmlformat, regardless of the –semicolon-rule setting.

As part of these semicolon rule improvements, the handling of EmptyStatements has also been refined: semicolons following control structures without bodies—such as if, for, or while—now appear directly after the closing parenthesis, rather than on a new, indented line. Additionally, multiple consecutive empty statements are collapsed into a single semicolon for a cleaner result.


import QtQuick
Item {
    Component.onCompleted: {
        for(;;);;
        ;;
        if (true);;;
        
        ;
        while (true);
        ;
        ;;
        var a = [1, 2, 3];;;;
        for (var i in a);;;;
    }
}

// qmlformat output prior to 6.10
import QtQuick

Item {
    Component.onCompleted: {
        for (; ; )
            ;
        ;
        ;
        ;
        if (true)
            ;
        ;
        ;

        ;
        while (true)
            ;
        ;
        ;
        ;
        var a = [1, 2, 3];
        ;
        ;
        ;
        for (var i in a)
            ;
        ;
        ;
        ;
    }
}

// qmlformat output as of 6.10
import QtQuick
Item {
    Component.onCompleted: {
        for (; ; );
        if (true);
        while (true);
        var a = [1, 2, 3];
        for (var i in a);
    }
}

Customizable Semicolon in Action

// original test.js
let v = a;;;;;;
[1, 2, 3].forEach(x => console.log(x));;;;;
;;;
;;;
let x = v;;;
x = 10;;;
;
;;;
;
let y = x;;;
(function() { })();;
let a = b
+ c
;
let xx = y;
-x;

qmlformat --semicolon-rule=always test.js adds semicolons at the end of JS statements.

let v = a;
[1, 2, 3].forEach(x => console.log(x));
let x = v;
x = 10;
let y = x;
(function () {})();
let a = b + c;
let xx = y;
-x;

qmlformat --semicolon-rule=essential test.js removes all semicolons, except those whose removal could lead to unintended behavior.

For example, the semicolon after let v = a; is not removed because the following line starts with a [ token:

let v = a;
[1, 2, 3].forEach(x => console.log(x))
let x = v
x = 10
let y = x;
(function () {})()
let a = b + c
let xx = y;
-x

In JavaScript, certain tokens at the beginning of a line—such as (, [, +, or -—can prevent automatic semicolon insertion (ASI) from working as expected. To avoid changing the semantics of the code, qmlformat preserves semicolons in these cases.

qmlformat Integration in Qt Creator 17

We’re pleased to announce that qmlformat is now integrated directly into Qt Creator 17, making QML code formatting more accessible and consistent across projects.

When you select qmlformat as your QML code formatter in Preferences under Qt Quick > Code Style, Qt Creator automatically detects the latest version of qmlformat available on your system. It generates a default configuration by executing the command qmlformat --write-defaults. You can customize this configuration based on your formatting preferences; additional information is available here. The global configuration is stored at QStandardPaths::GenericConfigLocation/.qmlformat.ini.

It’s important to understand how qmlformat locates its configuration settings. During formatting, it first looks for a .qmlformat.ini file in the directory of your source file. If no local configuration is found there, it searches up the directory hierarchy until it either finds a suitable configuration file or reaches the root directory. If no local configuration is found, it falls back to the global configuration stored in the generic config location.

This flowchart illustrates how qmlformat searches for its settings file:

[Start: QML file directory]
↓
[Is .qmlformat.ini here?] → Yes → [Use it]
↓ No
[Go to parent directory]
↓
[Repeat until root]
↓
[Still not found?] → [Use global config in GenericConfigLocation]

If you notice unexpected formatting behavior—formatting that doesn’t align with the settings you’ve defined in Qt Creator’s preferences—check whether a .qmlformat.ini file exists somewhere in your project’s directory tree. Such a file takes precedence over the global configuration and may override your intended style settings.

qmlformat in Action in Qt Creator 17

In Qt Creator 17, we’ve introduced a new preferences section for configuring QML formatting options. To use qmlformat with your QML files, you’ll need to explicitly select it as your preferred formatter.

pref

Setting up qmlformat in Qt Creator

  1. Navigate to Preferences > Qt Quick > Code Style
  2. Under the Formatter Selection dropdown, select QmlFormat[LSP]
  3. Adjust additional options like column width or import sorting according to your preferences under Global qmlformat configuration

Once you customize the Global qmlformat configuration, the preview on the right side will update to show how your settings will appear when formatting a QML file.

Using qmlformat in your QML files

After configuration, formatting your QML files is straightforward:

  1. Open any QML file in the editor
  2. Right-click to open the context menu
  3. Select Reformat Document

Alternatively, you can access the reformat action through Tools > QQml/JS > Reformat Document.

Note that if you’ve previously used Qt Creator’s Built-in Formatter, switching to qmlformat may produce different formatting results. This is expected as the two formatters use different algorithms and approaches to code styling. We recommend reviewing your code after the first formatting to ensure it meets your expectations.

Using Another Version of qmlformat


If you wish to use a specific version of qmlformat or your own fork, the Custom Formatter option is available. You’ll need to set the path and specify options in the arguments section. Please refer to the official qmlformat documentation for all available options.

Additionally, wrapping qmlformat with your own script can help fasten the workflow.  A simple shell wrapper allows you to customize qmlformat behavior for your needs. For example:


#!/bin/sh
# qmlformat-wrapper.sh

/path/to/your/qmlformat --indent-width 12 "$@"

chmod +x qmlformat-wrapper.sh
You can then use this script as your Custom Formatter by setting the script path and leaving the arguments section empty (or adding additional options as needed). Or simply point the Custom Formatter path directly to the qmlformat executable and pass your desired options in the arguments section:

 

customformatter

Note: Qt Creator currently uses its legacy formatter by default to preserve compatibility and user expectations. Because the two formatters are not guaranteed to produce identical output, using qmlformat by default in Qt Creator could lead to unexpected or unintentional changes in a user’s code style — especially in collaborative projects or existing codebases. Until the integration is more mature and aligned, Qt Creator leaves the choice to the user.

What’s Next?

With qmlformat now integrated into Qt Creator 17, working with QML just got a little easier. The new features—like automatic line breaking and import sorting—help keep your code clean, readable, and consistent. And since formatting is built right into Qt Creator, it’s just a couple of clicks away.

Looking ahead, we will be working on batch formatting for entire projects.

We’d love for you to give it a try and let us know what you think. Your feedback helps us make the QML tooling better for everyone.

Thanks for being part of the Qt community—happy coding with Qt Creator 17 and qmlformat!


Blog Topics:

Comments