diff --git a/addons/todo/__manifest__.py b/addons/todo/__manifest__.py
index ba50ea0aaf5..401bb97f71e 100644
--- a/addons/todo/__manifest__.py
+++ b/addons/todo/__manifest__.py
@@ -23,6 +23,7 @@
'uninstall_hook': '_todo_uninstall',
'assets': {
'web.assets_backend': [
+ 'todo/static/src/components/**/*',
'todo/static/src/scss/todo.scss',
'todo/static/src/views/**/*',
'todo/static/src/web/**/*',
diff --git a/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.js b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.js
new file mode 100644
index 00000000000..cbc7bc72c06
--- /dev/null
+++ b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.js
@@ -0,0 +1,57 @@
+/** @odoo-module */
+
+import { useState, onRendered, useRef } from "@odoo/owl";
+
+import { _lt } from "@web/core/l10n/translation";
+import { CharField } from "@web/views/fields/char/char_field";
+import { useAutoresize } from "@web/core/utils/autoresize";
+
+export class TodoEditableBreadcrumbName extends CharField {
+ setup() {
+ super.setup();
+ this.placeholder = _lt("Untitled to-do");;
+ this.input = useRef("input");
+
+ this.stateTodo = useState({
+ isUntitled: true,
+ });
+
+ onRendered(() => {
+ this.stateTodo.isUntitled = this._isUntitled(this.props.record.data[this.props.name]);
+ });
+ useAutoresize(this.input);
+ }
+
+ /**
+ * Check if the name is empty or is the generic name
+ * for untitled todos.
+ * @param {string} name
+ * @returns {boolean}
+ */
+ _isUntitled(name) {
+ name = name && name.trim();
+ return !name || name === this.placeholder.toString();
+ }
+
+ /**
+ * @private
+ * @param {InputEvent} ev
+ */
+ _onFocus(ev) {
+ if (this._isUntitled(ev.target.value)) {
+ ev.target.value = this.placeholder;
+ ev.target.select();
+ }
+ }
+
+ /**
+ * @private
+ * @param {InputEvent} ev
+ */
+ _onInput(ev) {
+ const value = ev.target.value;
+ this.stateTodo.isUntitled = this._isUntitled(value);
+ }
+}
+
+TodoEditableBreadcrumbName.template = 'todo.TodoEditableBreadcrumbName';
diff --git a/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.scss b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.scss
new file mode 100644
index 00000000000..6d70e1dab9c
--- /dev/null
+++ b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.scss
@@ -0,0 +1,30 @@
+.o_todo_breadcrumb_name_input{
+ width: 100%;
+ text-overflow: clip;
+ overflow: hidden;
+ border: none;
+ padding: 0px 5px 0px 2px;
+ color: $o-brand-secondary;
+
+ &:focus {
+ outline: 1px solid #ccc;
+ outline-offset: -1px;
+ color: $dark;
+ }
+
+ &.o-todo-untitled {
+ font-style: italic;
+ }
+
+ &.o-todo-untitled:focus {
+ font-style: normal;
+ }
+
+ &:disabled {
+ background-color: inherit;
+ }
+}
+
+.o_todo_breadcrumb_name_input:hover {
+ border-bottom: $input-border-width solid $o-brand-primary;
+}
diff --git a/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.xml b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.xml
new file mode 100644
index 00000000000..2f38217d305
--- /dev/null
+++ b/addons/todo/static/src/components/todo_editable_breadcrumb_name/todo_editable_breadcrumb_name.xml
@@ -0,0 +1,14 @@
+
+