Skip to main content
iVentureTeam

image

The image widget displays and uploads pictures on binary fields, with more options than any other display widget, including a WebP pipeline that quietly generates five sizes per upload.

August 11, 2026Updated August 11, 20266 min read
Odoo 19 product form showing the image widget with the product photo, upload and clear controls visible on hover.
Studio nameImage
Technical nameimage
Field typesbinary, many2one
Viewsform, list, kanban
Moduleweb, present in every Odoo database
Used in core92 occurrences across 39 modules, including product, website_sale, hr, event, pos views
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupYes, via Odoo Studio (Enterprise)
Alternativesimage_url, binary, many2one_avatar, contact_image

What the Image widget does

Anywhere Odoo shows a picture stored on the record, product photos, employee badges, event banners, this widget is rendering it. On an editable form it also carries the whole upload flow: pick a file, see it immediately, clear it, replace it.

Two abilities set it apart from a plain binary display. It can sit on a many2one and show an image from the related record, the pattern kanban cards use for the salesperson's avatar; the source enforces that usage by throwing an error unless preview_image names the related image field. And it detects the image type from the first character of the base64 payload, the magic-word technique, so JPEG, PNG, GIF, SVG and WebP all render without the record storing a mimetype.

Display is capped, not stretched: the size presets and width/height it derives set CSS max sizes, so images scale down to fit and never upscale past their pixels.

What this means for your team

Images carry more purchase and recognition weight than any field type, and they are also the heaviest thing your database serves. This widget sits exactly on that trade-off, and its options are the knobs: size keeps list rows light, zoom gives buyers the detail view without a click, convert_to_webp cuts image weight by half or more at upload time rather than at page-serve time.

The WebP option deserves a business note on its own. Enabling it on product images means every upload lands with pre-generated smaller versions and report-safe JPEG copies. Websites get modern formats, PDF quotations keep working, and nobody runs a batch conversion later. For catalog-heavy shops this is the difference between a fast product page and a slow one, decided at data entry.

Our rule on implementations: set an explicit size in every list and kanban, enable zoom only where inspection matters (products yes, avatars no), and turn on WebP conversion for any model whose images reach the website.

Setting it up in Odoo Studio (no code)

Studio covers the common case fully.

  1. Open the form in Studio and drag an Image field from the Add a field panel, or select an existing binary field and set Widget to Image.

  2. In Properties, pick the display Size (Small, Medium, Large) and toggle Enable zoom as needed.

What Studio cannot do here

The interesting options stay in XML: convert_to_webp for the upload pipeline, preview_image for the many2one usage, accepted_file_extensions to restrict file pickers, and zoom_delay for hover timing. All are one-line additions shown below.

Supported options in Odoo 19

Verified against image_field.js in the Odoo 19.0 web module, including one option that appears only in extractProps and nowhere in the declared list.

OptionTypeWhat it does
sizeselection / [w,h]Display cap as a [width, height] pair, 0 meaning unconstrained: Small [0,90], Medium [0,180], Large [0,270], custom pairs allowed. Max sizes only; images never upscale.
zoombooleanShows a full-size preview tooltip on hover.
zoom_delaynumberMilliseconds before the zoom preview appears.
convert_to_webpbooleanConverts uploads to WebP client-side and generates resized attachments plus JPEG copies for reports. GIF, SVG and WebP sources pass through.
accepted_file_extensionsstringRestricts the file picker, e.g. ".png,.svg".(default: image/*)
preview_imagefield nameBinary field to display. Mandatory when the widget sits on a many2one (the source throws without it); on a binary field it can point at a smaller variant like image_128 for lighter lists.
reloadbooleanRefreshes the image URL on record changes. Setting False freezes the first URL, occasionally useful in kanban cards that flicker.(default: true)
img_classstringExtra CSS classes on the img element. Read in extractProps only; it appears in no options panel or documentation.

The size values are [width, height] pairs where 0 means unconstrained. The presets translate to heights: Small [0,90], Medium [0,180], Large [0,270]. A custom pair like [120,120] is legal in XML. Whichever side is 0 scales automatically, and the values are max sizes, never upscaling.

Working examples

Product photo with zoom

<field name="image_1920" widget="image"
       options="{'size': [0, 270], 'zoom': True, 'zoom_delay': 300}"/>

Large preset with hover zoom after 300 ms. The zoom preview uses the full-resolution image.

WebP conversion at upload

<field name="image_1920" widget="image"
       options="{'convert_to_webp': True}"/>

JPEG and PNG uploads are converted in the browser; GIF, SVG and existing WebP pass through untouched, exactly as the source's exclusion list says.

Image from a related record

<field name="user_id" widget="image"
       options="{'preview_image': 'avatar_128', 'size': [0, 90]}"/>

A many2one rendered as the related record's image. preview_image is mandatory here; without it the widget raises an error rather than guessing.

Restrict what can be uploaded

<field name="logo" widget="image"
       options="{'accepted_file_extensions': '.png,.svg'}"/>

The WebP upload pipeline

Enable convert_to_webp and the upload handler does far more than convert, all verified in the source.

The browser first re-encodes the file to WebP on a canvas (skipping GIF, SVG and files already WebP). Then, because reports cannot embed WebP, Odoo builds a ladder of derivatives: resized copies at 1920, 1024, 512, 256 and 128 pixels, each stored as an ir.attachment, and for every size a JPEG twin flagged format: jpeg for PDF use, with transparency flattened to white.

Practical consequences. One image upload can create ten-plus attachments, so attachment counts on media-heavy models are expected, not a bug. The JPEG twins are why quotations with WebP product images still print. And because conversion happens client-side, a very large source image costs the uploader's browser a moment, not your server.

Unrelated but equally invisible: URLs carry a unique token derived from the record's write_date, which the widget declares as a field dependency. That is why replacing an image updates everywhere immediately instead of fighting browser caches, and why the avatar cache-bust arriving for users in Odoo 20 already works here.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. The development branch shows no behavior changes; see below.
Odoo 19.0VerifiedVerified against the shipped source and tested on a clean database.
Odoo 18.0VerifiedSame options and behavior. No XML changes needed.
Odoo 17.0VerifiedSame options and behavior. No XML changes needed.
Odoo 16.0VerifiedSame options and behavior. No XML changes needed.

Upgrade note. Options are stable across Odoo 16 to 19; views carry over. The WebP derivative pipeline arrived during this range, so re-uploading legacy images on an upgraded database is the cheapest way to backfill modern formats for the website.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The changes below are read from the public development branch and are not final until release.

Nothing changes for this widget. The development version of image_field.js declares the same seven options, the same alt attribute, the same many2one requirement and the same WebP pipeline. Internal component syntax moves to the new Owl props system, which affects JavaScript patches and nothing in XML. We re-verify once Odoo 20 ships.

Common problems and fixes

SymptomCause and fix
Widget on a many2one raises an errorpreview_image is mandatory for many2one usage; the source throws without it. Add options="{'preview_image': 'image_128'}" naming the related binary field.
Placeholder shows instead of the imageEmpty value, or the stored data failed to load as an image. Expected fallback; re-upload the file if the data is corrupt.
Old image keeps showing after replacementThe write_date cache-buster depends on the record actually saving. Save the record; the URL's unique token then changes and every view refreshes.
Attachment count exploded on media modelsThe WebP pipeline stores five sizes plus JPEG copies per upload, by design. Nothing to fix; it is the report-compatibility ladder described above.
PDF reports show blank where WebP images sitThe report references the WebP directly instead of the generated JPEG twin. Use the standard image helpers in the report so the JPEG derivative is picked.
Images look blurry in big cardsThe stored source is smaller than the display cap; the widget never upscales. Upload a larger original (e.g. store image_1920, display capped).

Image widget vs the alternatives

WidgetBest forKey difference
imagePictures stored on the record, with upload handlingFull option set: size caps, zoom, WebP pipeline, related-record display
image_urlImages hosted elsewhere, referenced by URLChar field with a link; nothing stored in the database
binaryNon-image files: PDFs, spreadsheets, any downloadFilename plus download button, no preview
many2one_avatarA partner or record shown as a small round photoFixed avatar styling, no upload from this widget
contact_imageContact-card style photo displaySpecialized presentation for partner forms

The practical test: image stored on the record, this widget. Image referenced by URL, image_url. Non-image files or downloads, binary. A person's picture specifically, prefer the avatar widgets, which add presence and consistent sizing for free.

Frequently asked questions

How do I control the size of an image field in Odoo?+
Use the size option: options="{'size': [0, 90]}" for small, [0,180] medium, [0,270] large, or any custom [width, height] pair with 0 meaning automatic. These are maximum sizes; images scale down, never up.
How do I enable image zoom on hover?+
Add options="{'zoom': True}", optionally with zoom_delay in milliseconds. The hover preview shows the full-resolution image, which is why product forms store image_1920 and display it capped.
What does convert_to_webp actually do?+
On upload, the browser re-encodes the image to WebP, and Odoo then generates resized attachment copies (1920 down to 128) plus JPEG versions for PDF reports, with transparency flattened to white. GIF, SVG and already-WebP files are left untouched. All verified in the source's upload handler.
Can the image widget show a picture from a related record?+
Yes, put it on the many2one and set preview_image to the related binary field, e.g. avatar_128. The option is mandatory in that usage; the widget throws an error without it.
Why do new uploads appear instantly everywhere?+
Image URLs include a token built from the record's write_date, which the widget declares as a dependency. Saving a new image changes the token, so browsers fetch fresh instead of serving cache.
How do I restrict which file types users can upload?+
Set accepted_file_extensions, e.g. options="{'accepted_file_extensions': '.png,.svg'}". The default accepts any image/* type.

Product images slowing your site down?

Right-sized images, WebP everywhere the web is served and JPEG where PDFs need it, configured once at upload time. We tune Odoo media handling end to end, from catalog forms to website speed, on Odoo 16 through 19.

Book a free consultation

How this page was produced

The option table, the size-pair semantics, the many2one requirement, the magic-word type detection and the full WebP attachment pipeline were read from the Odoo 19.0 web module source (image_field.js) and confirmed on a clean Odoo 19 database, where the screenshot was captured. The Odoo 20 statement comes from the same file on the public development branch, where it is unchanged. Spotted an error? Tell us and we will correct the page.