diff --git a/CMakeLists.txt b/CMakeLists.txt
index 608346e7536e3749fc28c8a3154ff2cf289beb34..7c44c6b42eafe9b6578a31ef0dd284ddf2f9bb88 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,10 +19,12 @@ find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
 qt_standard_project_setup(REQUIRES 6.5)
 
 qt_add_executable(mobile-datovka
+    src/app_version_info.cpp
     src/auxiliaries/email_helper.cpp src/auxiliaries/email_helper.h
     src/auxiliaries/ios_helper.cpp src/auxiliaries/ios_helper.h
     src/backup_zip.cpp src/backup_zip.h
     src/crypto/crypto.c src/crypto/crypto.h
+    src/datovka_shared/app_version_info.cpp src/datovka_shared/app_version_info.h
     src/datovka_shared/compat/compiler.h
     src/datovka_shared/compat_qt/misc.h
     src/datovka_shared/compat_qt/random.cpp src/datovka_shared/compat_qt/random.h
@@ -232,6 +234,7 @@ qt_add_qml_module(mobile-datovka
         res/../qml/components/AccessibleTextInfoSmall.qml
         res/../qml/components/AccessibleToolButton.qml
         res/../qml/components/AccountList.qml
+        res/../qml/components/ChangeLogBox.qml
         res/../qml/components/ControlGroupItem.qml
         res/../qml/components/DataboxList.qml
         res/../qml/components/FileDialogue.qml
diff --git a/mobile-datovka.pro b/mobile-datovka.pro
index b6c4c4a72bce217553a26d2e9be184670c10d145..734d849a8c160c751c4d6355cef3f349fc812a31 100644
--- a/mobile-datovka.pro
+++ b/mobile-datovka.pro
@@ -164,9 +164,11 @@ TRANSLATIONS_FILES += \
     res/locale/datovka_uk.qm
 
 SOURCES += \
+    src/app_version_info.cpp \
     src/auxiliaries/email_helper.cpp \
     src/auxiliaries/ios_helper.cpp \
     src/backup_zip.cpp \
+    src/datovka_shared/app_version_info.cpp \
     src/datovka_shared/compat_qt/random.cpp \
     src/datovka_shared/graphics/colour.cpp \
     src/datovka_shared/gov_services/helper.cpp \
@@ -343,6 +345,7 @@ HEADERS += \
     src/auxiliaries/email_helper.h \
     src/auxiliaries/ios_helper.h \
     src/backup_zip.h \
+    src/datovka_shared/app_version_info.h \
     src/datovka_shared/compat/compiler.h \
     src/datovka_shared/compat_qt/misc.h \
     src/datovka_shared/compat_qt/random.h \
diff --git a/qml/components/ChangeLogBox.qml b/qml/components/ChangeLogBox.qml
new file mode 100644
index 0000000000000000000000000000000000000000..9b580a3770fe456aa5d58846ac9266497a1c2de5
--- /dev/null
+++ b/qml/components/ChangeLogBox.qml
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014-2025 CZ.NIC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations including
+ * the two.
+ */
+
+import QtQuick 2.7
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.2
+
+/*
+ * Provides a simple change log dialog.
+ */
+Popup {
+    id: root
+    anchors.centerIn: parent
+    modal: true
+
+    property int preferredMinWidth: 280
+
+    /* Public interface. */
+    function showChangeLog() {
+        changeLogText.text = settings.loadChangeLogText()
+        root.open()
+    }
+
+    background: Rectangle {
+        color: datovkaPalette.base
+        border.color: datovkaPalette.text
+        radius: defaultMargin
+    }
+
+    ColumnLayout {
+        spacing: defaultMargin * 2
+        AccessibleText {
+            font.bold: true
+            Layout.alignment: Qt.AlignCenter
+            color: datovkaPalette.highlightedText
+            text: qsTr("Version") + ": " + Qt.application.version
+        }
+        AccessibleText {
+            Layout.preferredWidth: root.preferredMinWidth
+            horizontalAlignment : Text.Center
+            wrapMode: Text.Wrap
+            font.bold: true
+            text: qsTr("What's new?")
+        }
+        Rectangle {
+            Layout.preferredWidth: root.preferredMinWidth
+            implicitHeight: preferredMinWidth
+            color: "transparent"
+            border.color: datovkaPalette.line
+            border.width: 1
+            Flickable {
+                anchors.fill: parent
+                anchors.margins: 1
+                contentHeight: flickContent.implicitHeight
+                clip: true
+                Pane {
+                    id: flickContent
+                    anchors.fill: parent
+                    AccessibleText {
+                        id: changeLogText
+                        width: parent.width
+                        wrapMode: Text.WordWrap
+                        textFormat: TextEdit.RichText
+                    }
+                }
+                ScrollIndicator.vertical: ScrollIndicator {}
+            }
+        }
+        AccessibleButton {
+            Layout.alignment: Qt.AlignCenter
+            text: "OK"
+            onClicked: root.close()
+        }
+    }
+}
diff --git a/qml/main.qml b/qml/main.qml
index 7a3c4b7f95b25f873af190b4bfb464ef0d4821dd..3abb7b1ec6a6b18ca0fed9ea2a4065f8b2d6a924 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -262,6 +262,10 @@ ApplicationWindow {
         id: okMsgBox
     }
 
+    ChangeLogBox {
+        id: changeLogBox
+    }
+
     DrawerMenuDatovka {
         id: drawerMenuDatovka
     }
@@ -480,6 +484,10 @@ ApplicationWindow {
             target: settings
             function onRunOnAppStartUpSig() {
                 console.log("Running actions after application start-up.")
+                var isNewVersion = settings.isNewVersion()
+                if (isNewVersion) {
+                    changeLogBox.showChangeLog()
+                }
                 var areNews = messages.checkNewDatabasesFormat()
                 if (!areNews) {
                     pageView.push(pageConvertDatabase, {
diff --git a/qml/pages/PageAboutApp.qml b/qml/pages/PageAboutApp.qml
index 7d661a8c9ed5d8a39d31dadbea4c029c22bf5200..8d47d48eeb97b6cbb2123f912d365dd864f4b78c 100644
--- a/qml/pages/PageAboutApp.qml
+++ b/qml/pages/PageAboutApp.qml
@@ -151,6 +151,12 @@ Page {
                             + "<br/>"
                             + "<a href=\"mailto:datovka@labs.nic.cz?Subject=[Mobile Datovka%20" + settings.appVersion() + "]\">datovka@labs.nic.cz</a>"
                     }
+                    AccessibleButton {
+                        anchors.horizontalCenter: parent.horizontalCenter
+                        text: qsTr("Show News")
+                        accessibleName: qsTr("What's new?")
+                        onClicked: changeLogBox.showChangeLog()
+                    }
                 } // Column
             } // Pane
         } // Flickable
diff --git a/res/qml.qrc b/res/qml.qrc
index c44523fa6cb76dff68f5305314196933e8888260..8f36184fd3f08f2bd6c8ab534b7bf0ee7fea8816 100644
--- a/res/qml.qrc
+++ b/res/qml.qrc
@@ -18,6 +18,7 @@
         <file>../qml/components/AccessibleTextInfoSmall.qml</file>
         <file>../qml/components/AccessibleToolButton.qml</file>
         <file>../qml/components/AccountList.qml</file>
+        <file>../qml/components/ChangeLogBox.qml</file>
         <file>../qml/components/ControlGroupItem.qml</file>
         <file>../qml/components/DataboxList.qml</file>
         <file>../qml/components/FileDialogue.qml</file>
diff --git a/src/app_version_info.cpp b/src/app_version_info.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..685d3310454fb634a7a40706840c045d73122409
--- /dev/null
+++ b/src/app_version_info.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014-2025 CZ.NIC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations including
+ * the two.
+ */
+
+#include <QStringBuilder>
+
+#include "src/datovka_shared/app_version_info.h"
+
+#define textLineNL(text) \
+	(QLatin1String("<style>li{margin-left:-30px;}</style><ul><li>") % (text) % QLatin1String("</li></ul>"))
+
+QString AppVersionInfo::releaseNewsText(void)
+{
+	QString content;
+
+	content.append(textLineNL(tr("Showing news. :)")));
+
+	return content;
+}
diff --git a/src/datovka_shared/app_version_info.cpp b/src/datovka_shared/app_version_info.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..66ee35067c7ba3ce1f9877715c1728db1c217d88
--- /dev/null
+++ b/src/datovka_shared/app_version_info.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014-2025 CZ.NIC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations including
+ * the two.
+ */
+
+#include <QRegularExpression>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
+#  include <QVersionNumber>
+#else /* < Qt-5.6 */
+#  include <QVector>
+#endif /* >= Qt-5.6 */
+
+#include "src/datovka_shared/app_version_info.h"
+#include "src/datovka_shared/log/log.h"
+
+/* Release version string. */
+static const QRegularExpression releaseVerExpr(
+    QLatin1String("[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*"));
+/* Test build version string. */
+static const QRegularExpression gitAchiveVerExr(
+    QLatin1String("[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9a-f][0-9a-f]*"));
+
+/*!
+ * @brief Check whether trimmed \a str contains exactly a string matching \a re.
+ *
+ * @param[in] str String to be checked for match.
+ * @param[in] re Regular expression.
+ * @return True if trimmed \a str matches \a re.
+ */
+static inline
+bool trimmedMatchesRegExpr(const QString &str, const QRegularExpression &re)
+{
+	const QString trimmedStr = str.trimmed();
+
+	QRegularExpressionMatch match(re.match(trimmedStr));
+
+	return match.hasMatch() && (match.capturedLength() == trimmedStr.length());
+}
+
+bool AppVersionInfo::isReleaseVersionString(const QString &vStr)
+{
+	return trimmedMatchesRegExpr(vStr, releaseVerExpr);
+}
+
+bool AppVersionInfo::isGitArchiveString(const QString &vStr)
+{
+	return trimmedMatchesRegExpr(vStr, gitAchiveVerExr);
+}
+
+/*!
+ * @brief Strip unwanted data from version string.
+ *
+ * @param[in,out] vStr Version string. Must contain a substring in format
+ *                     '[0-9]+.[0-9]+.[0-9]+' .
+ * @return True if such substring is found.
+ */
+static
+bool stripVersionString(QString &vStr)
+{
+	vStr.remove(QRegularExpression(QLatin1String("^[^0-9.]*")));
+	vStr.remove(QRegularExpression(QLatin1String("[^0-9.].*$")));
+	vStr.remove(QRegularExpression(QLatin1String("^[.]*")));
+	vStr.remove(QRegularExpression(QLatin1String("[.]*$")));
+
+	QRegularExpressionMatch match(releaseVerExpr.match(vStr));
+
+	return match.hasMatch() && (match.capturedLength() == vStr.length());
+}
+
+#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
+#  warning "Compiling against version < Qt-5.6 which does not have QVersionNumber."
+
+/*!
+ * @brief Replacement for QVersionNumber which is not present in Qt before 5.6.
+ */
+class QVersionNumber {
+public:
+	QVersionNumber(void) : m_major(-1), m_micro(-1), m_minor(-1)
+	{
+	}
+
+	bool isNull(void) const
+	{
+		return (m_major < 0) || (m_micro < 0) || (m_minor < 0);
+	}
+
+	static
+	int compare(const QVersionNumber &v1, const QVersionNumber &v2)
+	{
+		if (v1.m_major < v2.m_major) {
+			return -1;
+		} else if (v1.m_major > v2.m_major) {
+			return 1;
+		} else if (v1.m_micro < v2.m_micro) {
+			return -1;
+		} else if (v1.m_micro > v2.m_micro) {
+			return 1;
+		} else if (v1.m_minor < v2.m_minor) {
+			return -1;
+		} else if (v1.m_minor > v2.m_minor) {
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+
+	static
+	QVersionNumber fromString(const QString &str)
+	{
+		QVersionNumber verNum;
+
+		const int elemNum = 3;
+		QStringList elemList(str.split(QChar('.')));
+		if (elemList.size() != elemNum) {
+			return verNum;
+		}
+
+		QVector<int> elemVect(3, -1);
+		for (int i = 0; i < elemNum; ++i) {
+			bool ok = false;
+			elemVect[i] = elemList[i].toInt(&ok);
+			if (!ok) {
+				elemVect[i] = -1;
+			}
+		}
+
+		verNum.m_major = elemVect[0];
+		verNum.m_micro = elemVect[1];
+		verNum.m_minor = elemVect[2];
+		return verNum;
+	}
+
+private:
+	int m_major;
+	int m_micro;
+	int m_minor;
+};
+#endif
+
+int AppVersionInfo::compareVersionStrings(const QString &vStr1, const QString &vStr2)
+{
+	QString vs1(vStr1), vs2(vStr2);
+
+	if (Q_UNLIKELY(!stripVersionString(vs1))) {
+		logErrorNL("Cannot strip version string '%s'.",
+		    vs1.toUtf8().constData());
+		return -2;
+	}
+	if (Q_UNLIKELY(!stripVersionString(vs2))) {
+		logErrorNL("Cannot strip version string '%s'.",
+		    vs2.toUtf8().constData());
+		return 2;
+	}
+
+	QVersionNumber v1 = QVersionNumber::fromString(vs1);
+	if (Q_UNLIKELY(v1.isNull())) {
+		logErrorNL(
+		    "Version string '%s' doesn't match required format.",
+		    vs1.toUtf8().constData());
+		return -2;
+	}
+	QVersionNumber v2 = QVersionNumber::fromString(vs2);
+	if (Q_UNLIKELY(v2.isNull())) {
+		logErrorNL(
+		    "Version string '%s' doesn't match required format.",
+		    vs2.toUtf8().constData());
+		return 2;
+	}
+
+	/*
+	 * Documentation of QVersionNumber::compare() only mentions negative
+	 * or positive values. It doesn't mention -1 or 1.
+	 */
+	int cmp = QVersionNumber::compare(v1, v2);
+	if (cmp < 0) {
+		return -1;
+	} else if (cmp == 0) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
diff --git a/src/datovka_shared/app_version_info.h b/src/datovka_shared/app_version_info.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7b24ebc47c1079c2289bc4e693a4a0188ad7f48
--- /dev/null
+++ b/src/datovka_shared/app_version_info.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014-2025 CZ.NIC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations including
+ * the two.
+ */
+
+#pragma once
+
+#include <QCoreApplication> /* Q_DECLARE_TR_FUNCTIONS */
+#include <QString>
+
+class AppVersionInfo {
+	Q_DECLARE_TR_FUNCTIONS(AppVersionInfo)
+
+public:
+	/*!
+	 * @brief Check whether string contains only release version
+	 *     (eg. 4.25.0).
+	 *
+	 * @note The string may contain some leading and trailing white-space characters.
+	 *
+	 * @param[in] vStr Version string.
+	 * @return If \a vStr is a release version.
+	 */
+	static
+	bool isReleaseVersionString(const QString &vStr);
+
+	/*!
+	 * @brief Check whether string contains git development build version
+	 *     (eg. 4.25.0.9999.20250106.151005.ee2327675e2f1a9a).
+	 *
+	 * @note The string may contain some leading and trailing white-space characters.
+	 *
+	 * @param[in] vStr Version string.
+	 * @return If \a vStr is a git development build version.
+	 */
+	static
+	bool isGitArchiveString(const QString &vStr);
+
+	/*!
+	 * @brief Compare newest available version and application version.
+	 *
+	 * @param[in] vStr1 Version string.
+	 * @param[in] vStr2 Version string.
+	 * @retval -2 if vStr1 doesn't contain a suitable version string
+	 * @retval -1 if vStr1 is less than vStr2
+	 * @retval  0 if vStr1 is equal to vStr2
+	 * @retval  1 if vStr1 is greater than vStr2
+	 * @retval  2 if vStr2 doesn't contain a suitable version string
+	 */
+	static
+	int compareVersionStrings(const QString &vStr1, const QString &vStr2);
+
+	/*!
+	 * @brief Release news.
+	 *
+	 * @return Release news text.
+	 */
+	static
+	QString releaseNewsText(void);
+};
diff --git a/src/main.cpp b/src/main.cpp
index 44d9d51a51cb7e671d82d67d9b52d028ca200f7b..a4dbab8792d520d440593a910ffb22b18127ae78 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -194,6 +194,7 @@ const struct QmlTypeEntry qmlComponents[] = {
 	{ "FilterBar", 1, 0 },
 	{ "GovFormList", 1, 0 },
 	{ "GovServiceList", 1, 0 },
+	{ "ChangeLogBox", 1, 0 },
 	{ "MessageBox", 1, 0 },
 	{ "MessageList", 1, 0 },
 	{ "PageHeader", 1, 0 },
diff --git a/src/setwrapper.cpp b/src/setwrapper.cpp
index 3759a6803ecb80e5b2fb2eeba1e269f632f70ba0..f731a0c62eccce24431c060033787109114203f8 100644
--- a/src/setwrapper.cpp
+++ b/src/setwrapper.cpp
@@ -24,17 +24,18 @@
 #include <QtGlobal> /* QT_VERSION, qVersion() */
 
 #if defined (Q_OS_ANDROID)
-#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
-	#include "android_qt6/src/android_io.h"
-#else
-	#include "android/src/android_io.h"
-#endif
+#  if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#    include "android_qt6/src/android_io.h"
+#  else
+#    include "android/src/android_io.h"
+#  endif
 #endif
 
 #if defined (Q_OS_IOS)
-#include "ios/src/url_opener.h"
+#  include "ios/src/url_opener.h"
 #endif
 
+#include "src/datovka_shared/app_version_info.h"
 #include "src/datovka_shared/compat_qt/random.h"
 #include "src/datovka_shared/localisation/localisation.h"
 #include "src/datovka_shared/log/log.h"
@@ -782,3 +783,42 @@ bool GlobalSettingsQmlWrapper::useIosDocumentPicker(void)
 		return false;
 	}
 }
+
+bool GlobalSettingsQmlWrapper::isNewVersion(void)
+{
+	if (Q_UNLIKELY(Q_NULLPTR == GlobInstcs::prefsPtr)) {
+		Q_ASSERT(0);
+		return false;
+	}
+
+	bool isNew = true;
+	QString storedVersion;
+
+	/*
+	 * If running app doesn't have release version then don't check.
+	 */
+	if (Q_UNLIKELY(!AppVersionInfo::isReleaseVersionString(VERSION))) {
+		return false;
+	}
+
+	GlobInstcs::prefsPtr->strVal("application.notification_shown.last_version", storedVersion);
+
+	/*
+	 * If stored version is empty or non-release version string then behave
+	 * as having a new version.
+	 */
+	isNew = storedVersion.isEmpty()
+	    || (!AppVersionInfo::isReleaseVersionString(storedVersion))
+	    || (1 == AppVersionInfo::compareVersionStrings(VERSION, storedVersion));
+
+	if (isNew) {
+		GlobInstcs::prefsPtr->setStrVal("application.notification_shown.last_version", VERSION);
+	}
+
+	return isNew;
+}
+
+QString GlobalSettingsQmlWrapper::loadChangeLogText(void)
+{
+	return AppVersionInfo::releaseNewsText();
+}
diff --git a/src/setwrapper.h b/src/setwrapper.h
index 718863aa0129557b1eebcb66f68aede209398e0e..aa1a08e25dacebcb9d2c57d29081266acbf5a7c3 100644
--- a/src/setwrapper.h
+++ b/src/setwrapper.h
@@ -491,6 +491,25 @@ public:
 	Q_INVOKABLE static
 	bool useIosDocumentPicker(void);
 
+	/*!
+	 * @brief Check if new version after first startup.
+	 *
+	 * @note This check isn't performed if running app doesn't have a valid
+	 *     release version.
+	 *
+	 * @return True if it is new version after first startup.
+	*/
+	Q_INVOKABLE static
+	bool isNewVersion(void);
+
+	/*!
+	 * @brief Load changelog content.
+	 *
+	 * @return Changelog text.
+	*/
+	Q_INVOKABLE static
+	QString loadChangeLogText(void);
+
 signals:
 	/*!
 	 * @brief Send PIN verification result to QML.