diff --git a/addons/web/static/src/js/components/custom_checkbox.js b/addons/web/static/src/js/components/custom_checkbox.js
new file mode 100644
index 00000000000..bc98dd7bab2
--- /dev/null
+++ b/addons/web/static/src/js/components/custom_checkbox.js
@@ -0,0 +1,58 @@
+odoo.define('web.CustomCheckbox', function (require) {
+ "use strict";
+
+ const utils = require('web.utils');
+
+ const { Component } = owl;
+
+ /**
+ * Custom checkbox
+ *
+ * Component that can be used in templates to render the custom checkbox of Odoo.
+ *
+ *
+ *
+ * @extends Component
+ */
+ class CustomCheckbox extends Component {
+ /**
+ * @param {Object} [props]
+ * @param {string | number | null} [props.id]
+ * @param {boolean} [props.value=false]
+ * @param {boolean} [props.disabled=false]
+ * @param {string} [props.text]
+ */
+ constructor() {
+ super(...arguments);
+ this._id = `checkbox-comp-${utils.generateID()}`;
+ }
+ }
+
+ CustomCheckbox.props = {
+ id: {
+ type: [String, Number],
+ optional: 1,
+ },
+ disabled: {
+ type: Boolean,
+ optional: 1,
+ },
+ value: {
+ type: Boolean,
+ optional: 1,
+ },
+ text: {
+ type: String,
+ optional: 1,
+ },
+ };
+
+ CustomCheckbox.template = 'web.CustomCheckbox';
+
+ return CustomCheckbox;
+});
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 2b0ff6f31dd..db1e893c168 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -93,6 +93,21 @@
+
+
+
+
+
+
+
diff --git a/addons/web/static/tests/components/custom_checkbox_tests.js b/addons/web/static/tests/components/custom_checkbox_tests.js
new file mode 100644
index 00000000000..21d7ae53af2
--- /dev/null
+++ b/addons/web/static/tests/components/custom_checkbox_tests.js
@@ -0,0 +1,56 @@
+odoo.define('web.custom_checkbox_tests', function (require) {
+ "use strict";
+
+ const CustomCheckbox = require('web.CustomCheckbox');
+ const testUtils = require('web.test_utils');
+
+ const { createComponent, dom: testUtilsDom } = testUtils;
+
+ QUnit.module('Components', {}, function () {
+
+ QUnit.module('CustomCheckbox');
+
+ QUnit.test('test checkbox: default values', async function(assert) {
+ assert.expect(6);
+
+ const checkbox = await createComponent(CustomCheckbox, {});
+
+ assert.containsOnce(checkbox.el, 'input');
+ assert.containsNone(checkbox.el, 'input:disabled');
+ assert.containsOnce(checkbox.el, 'label');
+
+ const input = checkbox.el.querySelector('input');
+ assert.notOk(input.checked, 'checkbox should be unchecked');
+ assert.ok(input.id.startsWith('checkbox-comp-'));
+
+ await testUtilsDom.click(checkbox.el.querySelector('label'));
+ assert.ok(input.checked, 'checkbox should be checked');
+
+ checkbox.destroy();
+ });
+
+ QUnit.test('test checkbox: custom values', async function(assert) {
+ assert.expect(6);
+
+ const checkbox = await createComponent(CustomCheckbox, {
+ props: {
+ id: 'my-custom-checkbox',
+ disabled: true,
+ value: true,
+ text: 'checkbox',
+ }
+ });
+
+ assert.containsOnce(checkbox.el, 'input');
+ assert.containsOnce(checkbox.el, 'input:disabled');
+ assert.containsOnce(checkbox.el, 'label');
+
+ const input = checkbox.el.querySelector('input');
+ assert.ok(input.checked, 'checkbox should be checked');
+ assert.strictEqual(input.id, 'my-custom-checkbox');
+ assert.ok(input.checked, 'checkbox should be checked');
+
+ checkbox.destroy();
+ });
+ });
+});
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index c5e78394245..05c534f4056 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -284,6 +284,7 @@
+
@@ -715,6 +716,7 @@
+