diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index 3cadf6cf6fafc102b417b5c000c39575a75a30ed..925b405051e358a28a0273a154e51e9f36ed82ee 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -84,6 +84,7 @@ registry
     .add('many2one_barcode', relational_fields.Many2oneBarcode)
     .add('list.many2one', relational_fields.ListFieldMany2One)
     .add('kanban.many2one', relational_fields.KanbanFieldMany2One)
+    .add('many2one_avatar', relational_fields.Many2OneAvatar)
     .add('many2many', relational_fields.FieldMany2Many)
     .add('many2many_binary', relational_fields.FieldMany2ManyBinaryMultiFiles)
     .add('many2many_tags', relational_fields.FieldMany2ManyTags)
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 72476d74c921551f3d4a65ded6f4370ba95b7931..b5b6d7252e5bce3b9b0378428d7d15a283381399 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -959,6 +959,44 @@ var KanbanFieldMany2One = AbstractField.extend({
     },
 });
 
+/**
+ * Widget Many2OneAvatar is only supported on many2one fields pointing to a
+ * model which inherits from 'image.mixin'. In readonly, it displays the
+ * record's image next to the display_name. In edit, it behaves exactly like a
+ * regular many2one widget.
+ */
+const Many2OneAvatar = FieldMany2One.extend({
+    _template: 'web.Many2OneAvatar',
+
+    init() {
+        this._super.apply(this, arguments);
+        if (this.mode === 'readonly') {
+            this.template = null;
+            this.tagName = 'div';
+            this.className = 'o_field_many2one_avatar';
+            // disable the redirection to the related record on click, in readonly
+            this.noOpen = true;
+        }
+    },
+
+    //--------------------------------------------------------------------------
+    // Private
+    //--------------------------------------------------------------------------
+
+    /**
+     * @override
+     */
+    _renderReadonly() {
+        this.$el.empty();
+        if (this.value) {
+            this.$el.html(qweb.render(this._template, {
+                url: `/web/image/${this.field.relation}/${this.value.res_id}/image_128`,
+                value: this.m2o_value,
+            }));
+        }
+    },
+});
+
 //------------------------------------------------------------------------------
 // X2Many widgets
 //------------------------------------------------------------------------------
@@ -3308,8 +3346,9 @@ return {
     Many2oneBarcode: Many2oneBarcode,
     KanbanFieldMany2One: KanbanFieldMany2One,
     ListFieldMany2One: ListFieldMany2One,
+    Many2OneAvatar: Many2OneAvatar,
 
-    FieldX2Many : FieldX2Many,
+    FieldX2Many: FieldX2Many,
     FieldOne2Many: FieldOne2Many,
 
     FieldMany2Many: FieldMany2Many,
diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss
index f0d15adbd52de952ff7661c9034c098fe0f0e817..8152389c32ce803daa56f0c68f8e66ef8f5c8e1a 100644
--- a/addons/web/static/src/scss/fields.scss
+++ b/addons/web/static/src/scss/fields.scss
@@ -83,6 +83,17 @@
         }
     }
 
+    // Many2OneAvatar
+    &.o_field_many2one_avatar {
+        > img.o_m2o_avatar {
+            border-radius: 50%;
+            width: 19px;
+            height: 19px;
+            object-fit: cover;
+            margin-right: 4px;
+        }
+    }
+
     // Many2many tags
     &.o_field_many2manytags {
         flex-flow: row wrap;
diff --git a/addons/web/static/src/scss/kanban_view.scss b/addons/web/static/src/scss/kanban_view.scss
index 29cfade38574e192a7c1dc634643d4beee38b129..d24a928536ef4017c8e9e2f4aec2aff0ce8ff37b 100644
--- a/addons/web/static/src/scss/kanban_view.scss
+++ b/addons/web/static/src/scss/kanban_view.scss
@@ -280,6 +280,12 @@
             }
         }
 
+        .o_field_many2one_avatar {
+            img.o_m2o_avatar {
+                margin-right: 0;
+            }
+        }
+
         // Commonly used to place an image beside the text
         // (e.g. Fleet, Employees, ...)
         .o_kanban_image {
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index b06b84026200afd94ddb6e1e22a22222a2e5f810..3abde74ac5144b283a52518bcdf4ea3a3731c300 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -1346,6 +1346,12 @@
         <button type="button" t-if="!widget.noOpen" class="fa fa-external-link btn btn-secondary o_external_button" tabindex="-1" draggable="false" aria-label="External link" title="External link"/>
     </div>
 </t>
+
+<t t-name="web.Many2OneAvatar">
+    <img t-att-src="url" t-att-alt="value" class="o_m2o_avatar"/>
+    <span t-esc="value"/>
+</t>
+
 <t t-name="FieldReference" t-extend="FieldMany2One">
     <t t-jquery=".o_input_dropdown" t-operation="before">
         <select t-att-class="'o_input o_field_widget' + (widget.nodeOptions.hide_model and ' d-none' or '')">
diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js
index 04c934290a1eb50e6c15e0027a049a89bae3c529..c2c4942381a73e9054aa1f7c372d784ef0c1d056 100644
--- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js
+++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js
@@ -3204,6 +3204,108 @@ QUnit.module('fields', {}, function () {
                 "should be 1 column after the value change");
             form.destroy();
         });
+
+        QUnit.module('Many2OneAvatar');
+
+        QUnit.test('many2one_avatar widget in form view', async function (assert) {
+            assert.expect(10);
+
+            const form = await createView({
+                View: FormView,
+                model: 'partner',
+                data: this.data,
+                arch: '<form><field name="user_id" widget="many2one_avatar"/></form>',
+                res_id: 1,
+            });
+
+            assert.hasClass(form.$('.o_form_view'), 'o_form_readonly');
+            assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline');
+            assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]');
+
+            await testUtils.form.clickEdit(form);
+
+            assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+            assert.containsOnce(form, '.o_input_dropdown');
+            assert.strictEqual(form.$('.o_input_dropdown input').val(), 'Aline');
+            assert.containsOnce(form, '.o_external_button');
+
+            await testUtils.fields.many2one.clickOpenDropdown("user_id");
+            await testUtils.fields.many2one.clickItem("user_id", "Christine");
+            await testUtils.form.clickSave(form);
+
+            assert.hasClass(form.$('.o_form_view'), 'o_form_readonly');
+            assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine');
+            assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]');
+
+            form.destroy();
+        });
+
+        QUnit.test('many2one_avatar widget in form view, with onchange', async function (assert) {
+            assert.expect(7);
+
+            this.data.partner.onchanges = {
+                int_field: function (obj) {
+                    if (obj.int_field === 1) {
+                        obj.user_id = [19, 'Christine'];
+                    } else if (obj.int_field === 2) {
+                        obj.user_id = false;
+                    } else {
+                        obj.user_id = [17, 'Aline']; // default value
+                    }
+                },
+            };
+            const form = await createView({
+                View: FormView,
+                model: 'partner',
+                data: this.data,
+                arch: `
+                    <form>
+                        <field name="int_field"/>
+                        <field name="user_id" widget="many2one_avatar" readonly="1"/>
+                    </form>`,
+            });
+
+            assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+            assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline');
+            assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]');
+
+            await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 1);
+
+            assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine');
+            assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]');
+
+            await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 2);
+
+            assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), '');
+            assert.containsNone(form, 'img.o_m2o_avatar');
+
+            form.destroy();
+        });
+
+        QUnit.test('many2one_avatar widget in list view', async function (assert) {
+            assert.expect(5);
+
+            this.data.partner.records = [
+                { id: 1, user_id: 17, },
+                { id: 2, user_id: 19, },
+                { id: 3, user_id: 17, },
+                { id: 3, user_id: false, },
+            ];
+            const list = await createView({
+                View: ListView,
+                model: 'partner',
+                data: this.data,
+                arch: '<tree><field name="user_id" widget="many2one_avatar"/></tree>',
+            });
+
+            assert.strictEqual(list.$('.o_data_cell span').text(), 'AlineChristineAline');
+            assert.containsOnce(list.$('.o_data_cell:nth(0)'), 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]');
+            assert.containsOnce(list.$('.o_data_cell:nth(1)'), 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]');
+            assert.containsOnce(list.$('.o_data_cell:nth(2)'), 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]');
+            assert.containsNone(list.$('.o_data_cell:nth(3)'), 'img.o_m2o_avatar');
+
+            list.destroy();
+        });
     });
 });
 });
diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst
index 92c6a292ff96a421520e35bbca9d47a995b4a293..433409acd5e42df6f3254366bf66d87aeb8048ff 100644
--- a/doc/reference/javascript_reference.rst
+++ b/doc/reference/javascript_reference.rst
@@ -2139,6 +2139,15 @@ Relational fields
 
     - Supported field types: *many2one*
 
+- many2one_avatar (Many2OneAvatar)
+    This widget is only supported on many2one fields pointing to a model which
+    inherits from 'image.mixin'. In readonly, it displays the image of the
+    related record next to its display_name. Note that the display_name isn't a
+    clickable link in this case. In edit, it behaves exactly like the regular
+    many2one.
+
+    - Supported field types: *many2one*
+
 - kanban.many2one (KanbanFieldMany2One)
     Default widget for many2one fields (in kanban view). We need to disable all
     editing in kanban views.