Skip to main content
iVentureTeam

mail_composer_attachment_selector

The mail_composer_attachment_selector widget is the paperclip button in the email composer footer: a file picker that uploads straight to the message's thread and links the result into attachment_ids.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 25, 2026Updated August 25, 20265 min read
Technical namemail_composer_attachment_selector
Field typesmany2many
Viewsform
Modulemail, installed with Discuss and any messaging app
Used in core2 occurrences in the mail module: the email composer wizard and the Scheduled Message form
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0
No-code setupNo. This is an internal composer control with no Studio path.
Alternativesmany2many_binary, mail_composer_attachment_list, binary

What the Composer attachment button does

Odoo's email composer keeps its footer minimal: Send, Discard, a template picker, a schedule button, and a paperclip. That paperclip is this widget. It is registered on the composer's attachment_ids field but renders no pills, no list, and no preview, just a button wrapping Odoo's FileUploader component with multiUpload enabled.

When files are picked, each one is converted from its data URL to a File object and handed to the mail.attachment_upload service, which uploads it against the thread the message will be posted to. The resulting attachment id is then linked into attachment_ids through an x2many command, so the wizard record carries the attachment when the message is sent.

What this means for your team

For users this is invisible plumbing, which is the point: attach a file, hit send, and the file ends up on the right record's thread rather than orphaned in a wizard. For teams customizing the composer, the widget matters for the opposite reason. Because it resolves the target thread itself, from the composer's model and res_ids values or the active ids in context, any custom wizard that borrows this widget must supply those values or uploads will attach to the wrong thread.

It is also a useful pattern to copy: a many2many to ir.attachment rendered as nothing but an upload button is a clean UX for wizards where the file list is displayed elsewhere or not at all.

Working examples

The core pattern: composer footer

<footer>
    <button string="Send" name="action_send_mail" type="object"/>
    <field name="attachment_ids"
           widget="mail_composer_attachment_selector"
           invisible="not can_edit_body"/>
</footer>

This mirrors mail_compose_message_views.xml in Odoo 19: the button hides entirely when the template body is not editable.

Scheduled message form

<field name="attachment_ids" widget="mail_composer_attachment_selector"/>
<field name="scheduled_date" widget="datetime_scheduled_date"/>

On mail.scheduled.message the widget takes the target record from res_id instead of res_ids, the second of its two hardcoded model checks.

Two hardcoded model checks and the 18.0 chatter leak

The upload path depends on which model the widget sits on. On the composer wizard, it parses res_ids as JSON, falling back to context.active_ids because the composer stops storing ids past a size limit, and always uploads against the first id. On mail.scheduled.message, it reads the single res_id relation instead.

The second branch exists because of a bug fixed in 19. The uploader service pushes each attachment into a composer object, and in Odoo 18 that was always the thread's own composer, which the chatter observes. Scheduling a message with an attachment therefore made the file appear inside the record's live chatter composer as a leftover. Odoo 19 passes a throwaway { attachments: [] } object for scheduled messages, with a source comment spelling out the reason. If you see that symptom on 18, it is this, not your customization.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. Model fallback and icon changes visible on the development branch; see below.
Odoo 19.0VerifiedVerified against the shipped source, including the scheduled-message isolation fix.
Odoo 18.0VerifiedFirst release with the widget. Carries the chatter-leak quirk on scheduled messages described below.
Odoo 17.0Not availableWidget does not exist; the composer's attachment button was internal.
Odoo 16.0Not availableWidget does not exist.

Upgrade note. The widget appeared in Odoo 18; composers on 16 and 17 wired their attachment button differently, inside the composer component itself. Views that reference the widget name cannot be backported below 18 without also backporting the file.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The development branch is unstable and everything below is provisional until release, when this page is re-verified.

Three changes are visible at the time of writing. The thread model resolution gains a fallback, record.data.model || context.active_model, which makes the widget usable in wizards that never store a model field. The store access moves from mailStore.Thread to mailStore["mail.thread"], relevant to anyone patching the upload flow. And the paperclip swaps from FontAwesome to the new oi icon set with data-icon="attach_file", so CSS targeting fa-paperclip in a customized composer will stop matching.

Common problems and fixes

SymptomCause and fix
Clicking the paperclip does nothing in a custom wizardThe widget reads attachment_ids, model, and res_ids by literal name; your wizard likely names them differently. Name the many2many field attachment_ids and provide model plus res_ids or active_ids, or fork the component.
Attachment ends up on the wrong recordres_ids is empty so the widget fell back to context.active_ids, which held a different selection. Pass explicit res_ids into the composer context when launching it from code.
On Odoo 18, scheduled message attachments also appear in the chatter composerKnown 18.0 behavior: uploads pushed into the thread's observed composer object. Fixed in Odoo 19 with an isolated composer; on 18, remove the leftover manually or backport the fix.
Button missing from the composer footerThe core view hides it when can_edit_body is false, typically because the template locks the body. Check the template's edit permissions rather than the widget.
Options set in the view have no effectThe registration declares no options and no extractProps; everything in options is ignored. There is nothing to configure; behavior changes require extending the component.

Composer attachment button vs the alternatives

WidgetBest forKey difference
mail_composer_attachment_selectorComposer-style wizards where the file list lives elsewhereRenders only an upload button and uploads to the target thread, not just the wizard
many2many_binaryGeneral attachment fields users manage themselvesShows the attached files as chips with delete and download
mail_composer_attachment_listThe full composer's attachment stripLists and removes attachments but deliberately has no upload button
binaryA single file on a recordOne binary column instead of linked ir.attachment records

The rule of thumb: this widget when the file list is invisible or shown elsewhere, many2many_binary when users need to see and manage what they attached, mail_composer_attachment_list only inside the full composer where it is designed to live.

Frequently asked questions

What is the mail_composer_attachment_selector widget?+
It is the paperclip button in the footer of Odoo's email composer and Scheduled Message form. It uploads one or more files against the message's target thread and links them into the composer's attachment_ids field. The button is the field's entire rendering.
Can I configure mail_composer_attachment_selector with options?+
No. It is registered with a bare component descriptor: no supported options, no extractProps. Its behavior is driven by hardcoded field names, attachment_ids, model, res_ids, res_id, and by the active_ids and is_thread_composer context keys.
Can I use this widget on my own wizard?+
Yes, if you follow its conventions: the field must be named attachment_ids, and the record must expose model plus either res_ids or active ids in context so the widget can resolve the thread to upload against. Otherwise extend the component.
Does it support attaching multiple files at once?+
Yes. The template enables multiUpload on the underlying FileUploader, so the file dialog accepts a multi-selection and each file is uploaded and linked in sequence. The button also binds the a hotkey.
Why do scheduled message attachments show up in the chatter on Odoo 18?+
An 18.0 quirk: the upload service pushed files into the thread's composer object, which the chatter observes. Odoo 19 passes an isolated composer for mail.scheduled.message uploads, with a source comment documenting exactly this problem.
Is mail_composer_attachment_selector available before Odoo 18?+
No. It was introduced in 18.0. Earlier composers rendered their attachment button inside the composer component itself rather than as a field widget.
What changes in Odoo 20?+
The development branch shows a fallback to context.active_model when the record has no model field, a store API rename, and the paperclip moving to the new oi icon set. All provisional until the September 2026 release.

Building a custom wizard around the composer?

Hardcoded field names, thread resolution, and upload services make Odoo's composer easy to use and tricky to extend. We build and debug custom mail flows, composer extensions, and wizard UX for companies on Odoo 16 through 19.

Book a free consultation

How this page was produced

This page was verified by reading mail_composer_attachment_selector.js and its template on the Odoo 19.0 branch, diffing against 18.0 and the public development branch, and checking both core usages in the composer wizard and Scheduled Message views. The screenshot was captured on a clean Odoo 19 database. Corrections are welcome via our contact page.