diff --git a/docs/vyos.vyos.vyos_config_module.rst b/docs/vyos.vyos.vyos_config_module.rst
index 1efbd38f..7b599129 100644
--- a/docs/vyos.vyos.vyos_config_module.rst
+++ b/docs/vyos.vyos.vyos_config_module.rst
@@ -159,6 +159,7 @@ Parameters
                 
@@ -234,6 +235,7 @@ Examples
 
     - name: render a Jinja2 template onto the VyOS router
       vyos.vyos.vyos_config:
+        match: smart
         src: vyos_template.j2
 
     - name: for idempotency, use full-form commands
diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py
index c35ff1ec..dba852db 100644
--- a/plugins/cliconf/vyos.py
+++ b/plugins/cliconf/vyos.py
@@ -49,11 +49,13 @@
 from ansible.errors import AnsibleConnectionFailure
 from ansible.module_utils._text import to_text
 from ansible.module_utils.common._collections_compat import Mapping
+from ansible.plugins.cliconf import CliconfBase
 from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
     NetworkConfig,
 )
 from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
-from ansible_collections.ansible.netcommon.plugins.plugin_utils.cliconf_base import CliconfBase
+
+from ansible_collections.vyos.vyos.plugins.cliconf_utils.vyosconf import VyosConf
 
 
 class Cliconf(CliconfBase):
@@ -256,6 +258,11 @@ def get_diff(
         if diff_match == "none":
             diff["config_diff"] = list(candidate_commands)
             return diff
+        if diff_match == "smart":
+            running_conf = VyosConf(running.splitlines())
+            candidate_conf = VyosConf(candidate_commands)
+            diff["config_diff"] = running_conf.diff_commands_to(candidate_conf)
+            return diff
 
         running_commands = [str(c).replace("'", "") for c in running.splitlines()]
 
@@ -328,7 +335,7 @@ def get_device_operations(self):
     def get_option_values(self):
         return {
             "format": ["text", "set"],
-            "diff_match": ["line", "none"],
+            "diff_match": ["line", "smart", "none"],
             "diff_replace": [],
             "output": [],
         }
diff --git a/plugins/cliconf_utils/__init__.py b/plugins/cliconf_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/cliconf_utils/vyosconf.py b/plugins/cliconf_utils/vyosconf.py
new file mode 100644
index 00000000..7a4fc63c
--- /dev/null
+++ b/plugins/cliconf_utils/vyosconf.py
@@ -0,0 +1,235 @@
+#
+# This file is part of Ansible
+#
+# Ansible 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.
+#
+# Ansible 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 Ansible.  If not, see .
+#
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+import re
+
+
+KEEP_EXISTING_VALUES = "..."
+
+
+class VyosConf:
+    def __init__(self, commands=None):
+        self.config = {}
+        if isinstance(commands, list):
+            self.run_commands(commands)
+
+    def set_entry(self, path, leaf):
+        """
+        This function sets a value in the configuration given a path.
+        :param path: list of strings to traveser in the config
+        :param leaf: value to set at the destination
+        :return: dict
+        """
+        target = self.config
+        path = path + [leaf]
+        for key in path:
+            if key not in target or not isinstance(target[key], dict):
+                target[key] = {}
+            target = target[key]
+        return self.config
+
+    def del_entry(self, path, leaf):
+        """
+        This function deletes a value from the configuration given a path
+        and also removes all the parents that are now empty.
+        :param path: list of strings to traveser in the config
+        :param leaf: value to delete at the destination
+        :return: dict
+        """
+        target = self.config
+        firstNoSiblingKey = None
+        for key in path:
+            if key not in target:
+                return self.config
+            if len(target[key]) <= 1:
+                if firstNoSiblingKey is None:
+                    firstNoSiblingKey = [target, key]
+            else:
+                firstNoSiblingKey = None
+            target = target[key]
+
+        if firstNoSiblingKey is None:
+            firstNoSiblingKey = [target, leaf]
+
+        target = firstNoSiblingKey[0]
+        targetKey = firstNoSiblingKey[1]
+        del target[targetKey]
+        return self.config
+
+    def check_entry(self, path, leaf):
+        """
+        This function checks if a value exists in the config.
+        :param path: list of strings to traveser in the config
+        :param leaf: value to check for existence
+        :return: bool
+        """
+        target = self.config
+        path = path + [leaf]
+        existing = []
+        for key in path:
+            if key not in target or not isinstance(target[key], dict):
+                return False
+            existing.append(key)
+            target = target[key]
+        return True
+
+    def parse_line(self, line):
+        """
+        This function parses a given command from string.
+        :param line: line to parse
+        :return: [command, path, leaf]
+        """
+        line = re.match(r"^('(.*)'|\"(.*)\"|([^#\"']*))*", line).group(0).strip()
+        path = re.findall(r"('.*?'|\".*?\"|\S+)", line)
+        leaf = path[-1]
+        if leaf.startswith('"') and leaf.endswith('"'):
+            leaf = leaf[1:-1]
+        if leaf.startswith("'") and leaf.endswith("'"):
+            leaf = leaf[1:-1]
+        return [path[0], path[1:-1], leaf]
+
+    def run_command(self, command):
+        """
+        This function runs a given command string.
+        :param command: command to run
+        :return: dict
+        """
+        [cmd, path, leaf] = self.parse_line(command)
+        if cmd.startswith("set"):
+            self.set_entry(path, leaf)
+        if cmd.startswith("del"):
+            self.del_entry(path, leaf)
+        return self.config
+
+    def run_commands(self, commands):
+        """
+        This function runs a a list of command strings.
+        :param commands: commands to run
+        :return: dict
+        """
+        for c in commands:
+            self.run_command(c)
+        return self.config
+
+    def check_command(self, command):
+        """
+        This function checkes a command for existance in the config.
+        :param command: command to check
+        :return: bool
+        """
+        [cmd, path, leaf] = self.parse_line(command)
+        if cmd.startswith("set"):
+            return self.check_entry(path, leaf)
+        if cmd.startswith("del"):
+            return not self.check_entry(path, leaf)
+        return True
+
+    def check_commands(self, commands):
+        """
+        This function checkes a list of commands for existance in the config.
+        :param commands: list of commands to check
+        :return: [bool]
+        """
+        return [self.check_command(c) for c in commands]
+
+    def quote_key(self, key):
+        """
+        This function adds quotes to key if quotes are needed for correct parsing.
+        :param key: str to wrap in quotes if needed
+        :return: str
+        """
+        if len(key) == 0:
+            return ""
+        if '"' in key:
+            return "'" + key + "'"
+        if "'" in key:
+            return '"' + key + '"'
+        if not re.match(r"^[a-zA-Z0-9./-]*$", key):
+            return "'" + key + "'"
+        return key
+
+    def build_commands(self, structure=None, nested=False):
+        """
+        This function builds a list of commands to recreate the current configuration.
+        :return: [str]
+        """
+        if not isinstance(structure, dict):
+            structure = self.config
+        if len(structure) == 0:
+            return [""] if nested else []
+        commands = []
+        for key, value in structure.items():
+            quoted_key = self.quote_key(key)
+            for c in self.build_commands(value, True):
+                commands.append((quoted_key + " " + c).strip())
+        if nested:
+            return commands
+        return ["set " + c for c in commands]
+
+    def diff_to(self, other, structure):
+        if not isinstance(other, dict):
+            other = {}
+            if len(structure) == 0:
+                return ([], [""])
+        if not isinstance(structure, dict):
+            structure = {}
+            if len(other) == 0:
+                return ([""], [])
+        if len(other) == 0 and len(structure) == 0:
+            return ([], [])
+
+        toset = []
+        todel = []
+        for key in structure.keys():
+            quoted_key = self.quote_key(key)
+            if key in other:
+                # keys in both configs, pls compare subkeys
+                (subset, subdel) = self.diff_to(other[key], structure[key])
+                for s in subset:
+                    toset.append(quoted_key + " " + s)
+                for d in subdel:
+                    todel.append(quoted_key + " " + d)
+            else:
+                # keys only in this, delete if KEEP_EXISTING_VALUES not set
+                if KEEP_EXISTING_VALUES not in other:
+                    todel.append(quoted_key)
+                continue  # del
+        for key, value in other.items():
+            if key == KEEP_EXISTING_VALUES:
+                continue
+            quoted_key = self.quote_key(key)
+            if key not in structure:
+                # keys only in other, pls set all subkeys
+                (subset, subdel) = self.diff_to(other[key], None)
+                for s in subset:
+                    toset.append(quoted_key + " " + s)
+
+        return (toset, todel)
+
+    def diff_commands_to(self, other):
+        """
+        This function calculates the required commands to change the current into
+        the given configuration.
+        :param other: VyosConf
+        :return: [str]
+        """
+        (toset, todel) = self.diff_to(other.config, self.config)
+        return ["delete " + c.strip() for c in todel] + ["set " + c.strip() for c in toset]
diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py
index eeb6bc44..3d0c18ef 100644
--- a/plugins/modules/vyos_config.py
+++ b/plugins/modules/vyos_config.py
@@ -67,6 +67,7 @@
     default: line
     choices:
     - line
+    - smart
     - none
   backup:
     description:
@@ -140,6 +141,7 @@
 
 - name: render a Jinja2 template onto the VyOS router
   vyos.vyos.vyos_config:
+    match: smart
     src: vyos_template.j2
 
 - name: for idempotency, use full-form commands
@@ -331,7 +333,7 @@ def main():
     argument_spec = dict(
         src=dict(type="path"),
         lines=dict(type="list", elements="str"),
-        match=dict(default="line", choices=["line", "none"]),
+        match=dict(default="line", choices=["line", "smart", "none"]),
         comment=dict(default=DEFAULT_COMMENT),
         config=dict(),
         backup=dict(type="bool", default=False),
diff --git a/tests/unit/cliconf/__init__.py b/tests/unit/cliconf/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/cliconf/test_utils_vyosconf.py b/tests/unit/cliconf/test_utils_vyosconf.py
new file mode 100644
index 00000000..c6a3264d
--- /dev/null
+++ b/tests/unit/cliconf/test_utils_vyosconf.py
@@ -0,0 +1,196 @@
+#
+# This file is part of Ansible
+#
+# Ansible 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.
+#
+# Ansible 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 Ansible.  If not, see .
+#
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+import unittest
+
+from ansible_collections.vyos.vyos.plugins.cliconf_utils.vyosconf import VyosConf
+
+
+class TestListElements(unittest.TestCase):
+    def test_add(self):
+        conf = VyosConf()
+        conf.set_entry(["a", "b"], "c")
+        self.assertEqual(conf.config, {"a": {"b": {"c": {}}}})
+        conf.set_entry(["a", "b"], "d")
+        self.assertEqual(conf.config, {"a": {"b": {"c": {}, "d": {}}}})
+        conf.set_entry(["a", "c"], "b")
+        self.assertEqual(
+            conf.config,
+            {"a": {"b": {"c": {}, "d": {}}, "c": {"b": {}}}},
+        )
+        conf.set_entry(["a", "c", "b"], "d")
+        self.assertEqual(
+            conf.config,
+            {"a": {"b": {"c": {}, "d": {}}, "c": {"b": {"d": {}}}}},
+        )
+
+    def test_del(self):
+        conf = VyosConf()
+        conf.set_entry(["a", "b"], "c")
+        conf.set_entry(["a", "c", "b"], "d")
+        conf.set_entry(["a", "b"], "d")
+        self.assertEqual(
+            conf.config,
+            {"a": {"b": {"c": {}, "d": {}}, "c": {"b": {"d": {}}}}},
+        )
+        conf.del_entry(["a", "c", "b"], "d")
+        self.assertEqual(conf.config, {"a": {"b": {"c": {}, "d": {}}}})
+        conf.set_entry(["a", "b", "c"], "d")
+        conf.del_entry(["a", "b", "c"], "d")
+        self.assertEqual(conf.config, {"a": {"b": {"d": {}}}})
+
+    def test_parse(self):
+        conf = VyosConf()
+        self.assertListEqual(
+            conf.parse_line("set a b c"),
+            ["set", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line('set a b "c"'),
+            ["set", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line("set a b 'c d'"),
+            ["set", ["a", "b"], "c d"],
+        )
+        self.assertListEqual(
+            conf.parse_line("set a b 'c'"),
+            ["set", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line("delete a b 'c'"),
+            ["delete", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line("del a b 'c'"),
+            ["del", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line("set a b '\"c'"),
+            ["set", ["a", "b"], '"c'],
+        )
+        self.assertListEqual(
+            conf.parse_line("set a b 'c' #this is a comment"),
+            ["set", ["a", "b"], "c"],
+        )
+        self.assertListEqual(
+            conf.parse_line("set a b '#c'"),
+            ["set", ["a", "b"], "#c"],
+        )
+
+    def test_run_commands(self):
+        self.assertEqual(
+            VyosConf(["set a b 'c'", "set a c 'b'"]).config,
+            {"a": {"b": {"c": {}}, "c": {"b": {}}}},
+        )
+        self.assertEqual(
+            VyosConf(["set a b c 'd'", "set a c 'b'", "del a b c d"]).config,
+            {"a": {"c": {"b": {}}}},
+        )
+
+    def test_build_commands(self):
+        self.assertEqual(
+            sorted(
+                VyosConf(
+                    [
+                        "set a b 'c a'",
+                        "set a c a",
+                        "set a c b",
+                        "delete a c a",
+                    ],
+                ).build_commands(),
+            ),
+            sorted(["set a b 'c a'", "set a c b"]),
+        )
+        self.assertEqual(
+            sorted(
+                VyosConf(
+                    [
+                        "set a b 10.0.0.1/24",
+                        "set a c ABCabc123+/=",
+                        "set a d $6$ABC.abc.123.+./=..",
+                    ],
+                ).build_commands(),
+            ),
+            sorted(
+                [
+                    "set a b 10.0.0.1/24",
+                    "set a c 'ABCabc123+/='",
+                    "set a d '$6$ABC.abc.123.+./=..'",
+                ],
+            ),
+        )
+
+    def test_check_commands(self):
+        conf = VyosConf(["set a b 'c a'", "set a c b"])
+        self.assertListEqual(
+            conf.check_commands(
+                ["set a b 'c a'", "del a c b", "set a b 'c'", "del a a a"],
+            ),
+            [True, False, False, True],
+        )
+
+    def test_diff_commands_to(self):
+        conf = VyosConf(["set a b 'c a'", "set a c b"])
+
+        self.assertListEqual(
+            conf.diff_commands_to(VyosConf(["set a c b"])),
+            ["delete a b"],
+        )
+        self.assertListEqual(
+            conf.diff_commands_to(VyosConf(["set a b 'c a'", "set a c b"])),
+            [],
+        )
+
+        self.assertListEqual(
+            conf.diff_commands_to(
+                VyosConf(
+                    [
+                        "set a b ...",
+                    ],
+                ),
+            ),
+            ["delete a c"],
+        )
+        self.assertListEqual(
+            conf.diff_commands_to(VyosConf(["set a ...", "set a d e"])),
+            ["set a d e"],
+        )
+        self.assertListEqual(
+            conf.diff_commands_to(VyosConf(["set a b", "set a c b"])),
+            ["delete a b 'c a'"],
+        )
+
+        self.assertListEqual(
+            conf.diff_commands_to(VyosConf(["set a b 'a c'", "set a c b"])),
+            ["delete a b 'c a'", "set a b 'a c'"],
+        )
+
+        self.assertListEqual(
+            VyosConf(
+                ["set a b c d", "set a b c e", "set a b d"],
+            ).diff_commands_to(VyosConf(["set a b c d", "set a b ..."])),
+            ["delete a b c e"],
+        )
+
+
+if __name__ == "__main__":
+    unittest.main()Choices:
                                     +line ←smartnone |