There is no argue that QML is an amazing technology to develop an outstanding user interface matching today’s trends. The QML language has beautiful and easy to learn syntax, but the code doesn’t structure itself that. By its nature, it can easily get messy. Therefore, you need to learn how to write clean QML code in order to keep your Qt software project easily maintainable. Ergo, to make savings on money and time.
What makes QML code clean?
Let’s start by answering the question what makes QML code clean? Can the code be actually dirty? Well, yes. There are a lot of things you can do wrongly without realizing it.
QML code formatting
The code style is the way code is formatted. It is how your code looks. I find this definition pretty plain and simple. It’s true that it doesn’t affect the application’s execution in anyway, but it significantly affects the readability of your code. So, it affects the application’s maintainability and therefore time future developers would need to make changes.
Code formatting is about indentation, use of white spaces and new lines, code grouping and organization. Many of those things are personal and there is no point in arguing about small things like where the opening bracket should be. The goal is to have consistent code formatting across the project. I have seen software developers who couldn’t be persistent in their own code, so it’s even more difficult when more people work on the same codebase. That’s why you should try automating code formatting.
Tip #1 for better QML code formatting
QML code has a tendency to have many nesting levels and it quickly spreads too much to the right. Therefore, my first tip would be to decrease the indent size to 2 spaces to limit this effect.
In order to do that, enter Qt Creator Options and navigate to Qt Quick settings. You should find a tab related to the code style. You cannot edit the default code style, so click on the Copy button in order to create your own style.

After giving your style a name click on the Edit button in order to modify the details of newly created style. I wish there were more options to adjust, but at least indentation settings can be changed here. You should see that both Tab size and Indent size equals 4. Decrease them to 2 and you are done. The change should be reflected in the code editor and by tools like Reformat file tool.

Tip #2 for better Qml code formatting
Another tip would be to use QML/JS Reformat file tool. How does it work? It takes your code and reformat it just to look nicer. Let’s have a look at the component below.
import QtQuick 2.0
Item
{
id: root
width:30;height:50;
Text {
id: textItem
font.pixelSize: 48; wrapMode: Text.WordWrap
lineHeight: 0.75
Behavior on color { ColorAnimation { duration: 120; easing.type: Easing.OutElastic} }
states: [State {
name: "pressed"
when: mouse.pressed
PropertyChanges {
target: textItem
color :Qt.lighter(root.color)
}}
]
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
// ...
}
}
function foo()
{
// ...
}}
It’s definitely not the most beautiful QML code that you have seen. There are several issues including indentations that are different from each other, wrong braces positions, spaces that are sometimes missing and sometimes redundant. To fix it quickly and easily, navigate to the Tools option from Qt Creator’s top bar menu and then find Reformat file under QML/JS group. Running the tool can save a lot of your time.
import QtQuick 2.0
Item {
id: root
width: 30
height: 50
Text {
id: textItem
font.pixelSize: 48
wrapMode: Text.WordWrap
lineHeight: 0.75
Behavior on color {
ColorAnimation {
duration: 120
easing.type: Easing.OutElastic
}
}
states: [
State {
name: "pressed"
when: mouse.pressed
PropertyChanges {
target: textItem
color: Qt.lighter(root.color)
}
}
]
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
// ...
}
}
function foo() {// ...
}
}
If you’re committed enough to give up Qt Creator some control over your QML code formatting, then try setting up Reformat file tool to be run automatically on file save.

QML coding conventions
Coding conventions go beyond the remit of formatting. The QML code can be formatted nicely and look okay from the code style perspective, but it may need polishing anyway. In Scythe Studio we are pretty restrictive about our coding conventions that slightly differ from official QML coding conventions, but they also add a lot of other concepts. However, these official conventions are good for you to start with, so following them is suggested.
Below you will find a few of the conventions that we follow in Scythe Studio company in order to provide high quality Qt Qml software development services and have clean QML code. We enforce following conventions even on job candidates while working on their technical assignments.
1. Root item id
In order to improve readability and traceability, setting an id of top-level item in a file to root is suggested. Then, in every single file you know that the reference to root is about top-level item.
import QtQuick 2.0
Item {
id: root
}
2. QML string translations
If the string is visible to the user always wrap it in qsTr() function. Also instead of using end translation as strings displayed by default use THIS_NOTATION. For example instead of writing text: qsTr(„This is displayed”) use text: qsTr(„THIS_DISPLAYED”). Thanks to this approach if any string is missing a translation we instantly can see that in UI.
3. Have a private object
Encapsulation and proper scoping should matter when you code in QML. Just like in C++ you do not want all members of your class to be public, so the same should apply to QML code. Not all properties and functions should be visible from the outside. There is often a need for helping properties and functions, but they have no value or can even cause some unexpected harm when used from outside. Therefore wrapping them into an internal QtObject is suggested.
QtObject {
id: _ // or id: internal
readonly property real timeout: 1000
property bool isExpanded: false
function addDestination() {
// ...
}
}
4. Order of various attributes
Official QML conventions cover order of attributes, but we just define it a bit differently and in more details. It simply pays off in big projects.
import QtQuick 2.15
import QtQuick.Controls 2.15 // Qt modules first
import com.scythestudio.ownmodule 1.0 // then own modules
import "./controls/" // then local directories
// A hypothetical custom button item.
Item {
id: root
/// The text to be placed on the button
property string title: "title" // properties declaration
/// The button's background color
property alias color: button.color
signal buttonClicked() // signal declaration
Button { // child items
id: button
width: parent.width // properties derived from Item
height: parent.height
icon.color: "black" // Button's own properties
onClicked: {
root.buttonClicked()
}
}
states: State { // states
name: "selected"
PropertyChanges { target: button; icon.color: "red" }
}
transitions: Transition { // transitions
from: ""
to: "selected"
ColorAnimation { target: button; duration: 200 }
}
PropertyAnimation { // special items
id: closeDelayAnimation
// ...
}
Timer { // special items
}
Connections { // special items
target: someCppController
function onProcessFailed() {
// ...
}
}
QtObject { // private item
id: _
readonly property real timeout: 1000
property bool isExpanded: false
function addDestination() {
// ...
}
}
onWidthChanged: { // slots
}
function close() { // public functions
isExpanded = false
closeDelayAnimation.start()
}
}
From the code snippet above you can deduce a lot of rules, but let’s highlight three:
- Functions definitions are preferred to be on the bottom of the file in order to see component’s children first and quicker understand how the item is built;
- States and transitions tend to grow fast, so that’s why they should be placed below children though they are also properties;
- Component’s public API should be documented;
Good QML practices
In the documentation of the Qt framework, you can find a list of best practices for QML and Qt Quick. I find all of them quite useful and some of them principle to have a clean QML code. Probably the most crucial practice described on that page is a rule to separate UI from logic. When you code using QML, you may be tempted to use QML and JavaScript for many things including application’s logic and network communication.
Try then to move such things to C++ as then you have a clear separation of the frontend written in QML and let’s say the backend written in C++. Therefore, your application’s architecture offers more scalability. One of the things that should almost always be written in C++ are data models. You can go with QML models only if your list is static. Otherwise, you will finally either lose your time on trying to perform advanced operations using QML models or on rewriting them to C++ models.
In Scythe Studio we conduct online and offline workshops dedicated to various aspects of Qt software development. One of the workshop topics is „How to design an architecture for your Qt Quick application?”. Contact us to learn more about our workshops offer.
Tip for easier handling QML issues
Another good practice is to use qmllint tool that would help you easier spot the issues in your Qml code. It’s a pretty useful tool warning about many issues including unused imports and unqualified access to properties. The Qt company is currently working on a better integration of this tool with Qt Creator IDE.
Why is it important to write clean QML code?
At this point you should know what clean code means and what you can do to ensure that the code you write follows these rules. So now let’s answer the question why is it so important to write clean QML code? It’s one of the factors that makes your code high quality. When you have a set of conventions and rules to follow, it’s easier to spot errors and therefore you immediately start to make time savings. Moreover, as clean code concepts include good practices, the developers who follow these directives write code according to the art. It doesn’t require then that much effort during the reviewing process.
When you code there are plenty of tiny decisions to make and after looking at the very same code after some time, you rather have no idea why you wrote the code in a particular way. On the other hand, when you follow clean QML code rules, you outsource those little decisions to the code. It frees up your thinking resources.
Finally, of course, the most important reason to write clean QML code is the fact that it significantly improves the maintainability of the project. Someone said that you write the code once, but it’s read multiple times. Therefore, you need to care about readability of your code as well as about all the good practices. Then it’s easier for you and other developers to jump into the project, understand a particular piece and make changes.
Now you may ask how strict about following clean QML code rules your team should be. Well, it all depends on the length of the project that you’re currently working on. It is also important how many people participate or will participate in the future. My point of view, it’s hard to overdo it when it comes to ensuring high quality code. Even if you prepare a small demo of which code will be public, it’s good to follow all these rules to be finally content with the results of your work. After a while, it gets into the blood and developers stop even thinking about it as an additional effort.
If you maintain a long-life project such as Qt framework, you have many reasons to be extremely restrictive about the code quality. I described my experience of contributing to Qt Charts in Qt 6.2 module in a blog post. There are a lot of conventions and rules to follow and it’s actually quite difficult at the beginning to get used to them. There are though CI/CD mechanisms that save reviewers time by, for example, looking for typos. At the beginning it was a struggle to make my code match all those requirements. Nevertheless, I know that it’s a must-have in this project and I can say that it was even enjoyable
Key takeaways and general rules
- Maintaining a clean QML codebase is an important aspect of a healthy Qt project;
- KISS – keep it short and simple. Not for you, but for the reader. Always choose the simpler implementation option over the more complicated one;
- Document public interfaces (API) of created items;
- Do not add comments describing how something is achieved. If the reader can’t deduce that from your code, then code needs refactoring. Comment on not obvious solutions that may look surprisingly;
- Do not re-invent the wheel;
- Adapt the code to the existing codebase. If you have improvement ideas, first discuss them with other developers;
- Follow the coding conventions and good QML practices;