Odoo 19 ships a copy of Font Awesome 4.7.0, a library released in October 2016. It lives in web/static/src/libs/fontawesome/, next to the real fonts, loaded through the same asset bundles. For a decade that filing made a kind of sense, because it genuinely is a font file. It is also not typography, and that distinction is the reason Odoo 20 deletes it.
This is a companion to the fonts series (part 1, part 2, part 3), which deliberately leaves icon fonts out. Everything below is the story those articles do not tell: where the name came from, what the licence split cost, what OCA’s base_fontawesome is actually for, and what replaces it.
Verified against odoo/odoo at commit e6bb2da on master (4 August 2026) and branch 19.0. Odoo 20.0 has not shipped; master is where it is being built, so what follows is the state of that work rather than a released version.
Why an icon set shipped as a font
Font Awesome’s first release was 3 March 2012. Dave Gandy built it for Twitter Bootstrap, which needed a reliable icon set and had none of its own. The name comes from a joke: an episode of the sitcom NewsRadio in which Stephen Root’s eccentric billionaire wants a mansion like Xanadu “but without a dorky name” and settles on Fort Awesome.
The delivery mechanism was a font because in 2012 that was the only format that solved the whole problem at once. Designers wanted icons that scaled on the retina displays then arriving, took their colour from the surrounding text, and worked in browsers back to IE8. A font does all three for free. Map each icon to a codepoint in Unicode’s Private Use Area, declare the family, and let CSS pull the glyph in:
.fa-check:before {
content: "\f00c";
}
The glyph inherits color, font-size and line-height from its parent, because as far as the browser is concerned it is a character. That is the entire trick, and it was a good one. It is also why the library is called a font, why it sits in a fonts directory, and why it keeps turning up in conversations about typography where it does not belong.
The costs surfaced later. Private Use Area codepoints mean assistive technology reads a decorative glyph as an unknown character unless the markup hides it. A glyph carries one colour, so multicolour icons are impossible. Icon fonts require a blocking @font-face request before any icon can paint, leaving every icon invisible or a fallback box until that font loads, whereas inline SVG paints with the surrounding markup. And when the font fails to load, every icon on the page becomes a box, which is exactly the failure mode part 1 describes for missing script coverage. Once SVG tooling matured, the reasons to encode pictures as text ran out. Font Awesome itself added an SVG-based toolkit in version 5, and today bills itself as “the iconic SVG, font, and CSS toolkit”, with SVG named first.
Odoo’s copy stopped in 2016
The header of addons/web/static/src/libs/fontawesome/css/font-awesome.css4 on branch 19.0 is unchanged from the day it was vendored:
/*!
* Based on Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
4.7.0 was the last release of the 4.x line. Nine years later, Odoo 19 still serves it.
The reason is visible in a single count. On branch 19.0 the string fa fa- appears 2,754 times across 797 XML files, and that is core alone, before any customer module. Every one of those is a call site bound to Font Awesome 4 class names. A library that cheap to add becomes expensive to remove in exact proportion to how useful it was.
The licence split that made upgrading a decision
Font Awesome 4 was uncomplicated. The font files were SIL OFL 1.1, the CSS and LESS were MIT, and the remaining project files were CC BY 3.0. The project had also dropped its attribution requirement three major versions earlier: “Attribution is no longer required as of Font Awesome 3.0 but is much appreciated.”
Version 5, released in December 2017, restructured all of it. The catalogue split into Free and Pro, with Pro a paid product, and the Free tier acquired a three-way licence that depends on file format rather than on the project as a whole:
- Icons packaged as SVG and JS files: CC BY 4.0, and attribution is mandatory again.
- Web and desktop font files: SIL OFL 1.1.
- Everything that is neither font nor icon: MIT.
Alongside the licensing, version 5 renamed the class prefixes. A single fa became fas, far and fab for solid, regular and brand styles, so upgrading was never a version bump. For a codebase with thousands of fa fa- call sites it was a mass rewrite plus a licensing review, in return for icons nobody was blocked on.
To be clear about what the record supports: Odoo’s commit history does not state why the library sat at 4.7.0 for a decade, and we are not claiming the licence change alone explains it. What the licence change does explain is why “just upgrade Font Awesome” was never the small task it sounds like.
base_fontawesome: OCA’s answer
That freeze is the gap base_fontawesome17 (OCA/server-tools) fills. Core bundles an old version and moving it takes a full release cycle, so the module swaps the asset instead. Its entire job is a manifest replacement, repeated for every bundle that loads the library:
"assets": {
"web.assets_backend": [
("replace", "web/static/src/libs/fontawesome/css/font-awesome.css",
"base_fontawesome/static/src/css/fontawesome.css"),
"base_fontawesome/static/lib/fontawesome-6.7.2/css/all.css",
"base_fontawesome/static/lib/fontawesome-6.7.2/css/v4-shims.css",
],
"web.assets_frontend": [ ... same replace ... ],
"web.report_assets_common": [ ... same replace ... ],
},
No models, no views, just a ("replace", …) tuple against web.assets_backend, web.assets_frontend and web.report_assets_common at once. Note v4-shims.css in that list: it is the compatibility layer that keeps Font Awesome 4 class names working against a version 6 library, which is the same problem the class renames created above, solved by the upstream project.
Hitting all three bundles is not thoroughness, it is structural. An icon glyph has to render identically in a backend button, a website page and a PDF header, so there is no per-layer variable to redefine the way part 1 redefines a font family. You replace the shared stylesheet everywhere it is loaded.
Its companion, base_fontawesome_web_editor18, repairs a side effect. Font Awesome 6.7.2 restructured its CSS to define icons through a --fa custom property instead of the selector patterns Odoo’s icon picker scans for, so the WYSIWYG editor stops finding any icons at all. The module patches the detection utility to look for the new property:
patch(fonts, {
fontIcons: [{base: "fa", parser: /\.(fa-(?:\w|-)+)/i}],
_isValidFontAwesomeRule(selectorText, cssText) {
return selectorText && cssText && cssText.includes("--fa:");
},
...
});
Versions: base_fontawesome is available from 12.0 through 18.0. base_fontawesome_web_editor covers 17.0 and 18.0.
Odoo 20 removes it
The replacement landed on master in PR #2736065, commit 3e15a7b6 and its message is unambiguous:
This commit removes the Font Awesome library and introduces Material Symbols as the main icon system. To cover icons not provided by Material Symbols, the Odoo UI Icon (OI) library is maintained (e.g., custom kanban view icons or brand icons such as Facebook or GitHub).
The OI library itself is not new to this migration. It shipped in Odoo 16.0 on 26 January 2022, designed to coexist with Font Awesome 4.7 and address a gap the old library had left. The original commit11 states: “The goal is to provide a wider range of icons specifically made for our UI and cover FontAwesome’s lack of iconography for specific businesses and functionalities.” That coexistence of two icon systems lasted four years; the 2026 migration collapsed them into one under Material Symbols, with OI now the fallback for everything Material Symbols does not carry.
The markup contract changes from a class per icon to an attribute:
<!-- before -->
<i class="fa fa-check"/>
<!-- after -->
<i class="oi" data-icon="check"/>
Odoo did not simply substitute one vendor for another. Material Symbols does not cover everything Font Awesome did, so Odoo’s own icon font absorbs the remainder. The compatibility map that encodes this decision, web/static/src/webclient/icons_mappings/fa_to_ms.scss12, is the clearest artefact of the whole migration: 792 Font Awesome 4 names, of which 585 map to a Material Symbols icon and 207 map to an Odoo UI Icon. Those 207 are mostly brand marks, the icons a general-purpose Google icon set has no reason to carry.
$fa-icon-map: (
"adjust": ("ms", "contrast"),
"align-center": ("ms", "format_align_center"),
"amazon": ("oi", "amazon"),
"ambulance": ("ms", "emergency"),
...
);
That map is explicitly a bridge, not a destination. The commit calls the mappings “temporary” and says they are “intended to be removed once the migration is complete.”
Two more commits finish the job. d4113ca8 cleans up the places that never used fa classes at all, where SCSS set the font family and the glyph charcode directly and so broke straight through the compatibility layer. Then c5a40a610, which closes the PR that prompted this article, #2568409, sweeps the call sites across the entire codebase, with matching PRs in enterprise, design-themes, upgrade and documentation.
The sweep worked. Against 2,754 occurrences of fa fa- on 19.0, master at the time of writing has 90, in 14 files, and 69 of those sit in a single vendored spreadsheet bundle. Core is done.
So what actually got better?
Writing an icon in Odoo used to require knowing things you should not have had to know.
Before, there were two icon systems and two ways to write them. A Font Awesome icon was class="fa fa-arrow-left". One of Odoo’s own icons was class="oi oi-arrow-left". Same job on screen, different prefix, and choosing correctly meant knowing which of the two libraries happened to hold the icon you wanted. That is not knowledge about your feature. It is knowledge about Odoo’s vendoring history.
Commit c5a40a610 states plainly what it collapsed:
Previous patterns such as
class="oi oi-fw oi-arrow-left"orclass="fa fa-fw fa-arrow-left"are migrated to the new format usingclass="oi oi-fw"with the data-icon attribute (e.g.,data-icon="west"), ensuring consistency with the Material Symbols-based system.
Now the class no longer says where the icon comes from. icons.scss13 calls .oi the “generic class for Material Symbols and OI extended library icons”, and the name in data-icon decides everything else. Icons Odoo still ships itself carry an oi_ prefix purely so they cannot collide with Google’s names:
<i class="oi" data-icon="check"/> <!-- Material Symbols -->
<i class="oi" data-icon="oi_amazon"/> <!-- Odoo's own, mostly brand marks -->
Three things follow from that, and they are the real return on the migration.
Filled and outlined stop being two different icons. Font Awesome shipped fa-star and fa-star-o as separate names, and the -o suffix convention had to be memorised per icon. The new base rule sets content: attr(data-icon) and appends _f when the element also carries oi-filled, so one name plus one modifier covers both. The compatibility map is where the old cost is visible. It contains 41 such pairs, two Font Awesome names each resolving to a single Material Symbols name that differ only by fill, "star": ("ms", "star", "filled") sitting directly above "star-o": ("ms", "star"). That is 82 of its 792 entries spent on a distinction the new system expresses with one class.
The icon name is now readable where you are looking. data-icon="west" resolves through a font ligature, meaning the icon name is the text and the font substitutes the glyph. Inspect an element and you see west. Under Font Awesome the name existed only as a charcode in a stylesheet, content: "\f060", so devtools showed you a private-use character and nothing else.
Adding an icon no longer touches CSS. Font Awesome needed one rule per icon to bind a class to its charcode, so every icon Odoo used cost another entry in a stylesheet that only ever grew. In the new system an icon is a line in icons_wishlist.txt and a run of generate_icons.py, which rebuilds the font and also emits ms_icons.js so the new icons are searchable in the media dialog. Right-to-left support went the same way: instead of ad-hoc flips, rtl_icons.scss14 is a plain list of directional icon names that get mirrored under .o_rtl.
Commit d4113ca8 shows what the old design cost when people worked around it. Some places skipped the classes entirely and set the font family and charcode straight in SCSS, which broke even through the compatibility layer, because a charcode is not a name and nothing could translate it. Those are gone now, replaced by named icons like everything else.
The one Font Awesome file that survives
Run the file list on master and one artefact is still standing, alone, with no stylesheet to load it (still there today):
addons/web/static/src/libs/fontawesome/fonts/fontawesome-webfont.ttf
It survives because of email. addons/mail/controllers/mail.py16 rasterises icons to PNG server-side, since mail clients cannot be relied on to load a web font and render a glyph from it:
@staticmethod
def _get_icon_rendering_info(icon, font):
# default to 'oi' (material icons)
info = {
'path': 'web/static/src/libs/materialsymbols/material_symbols_outlined_subset.woff2',
...
}
if font == 'oi' and icon.isdigit():
# custom odoo icon
info['path'] = 'web/static/lib/odoo_ui_icons/fonts/odoo_ui_icons.woff2'
...
elif font == 'fa':
# legacy fontawesome icon
info['path'] = 'web/static/src/libs/fontawesome/fonts/fontawesome-webfont.ttf'
A fa branch, commented as legacy on both lines, kept for stored icon values the migration did not rewrite. It is a fitting end: the last surviving use of Font Awesome in Odoo is the one case where the file is treated as what it always was, a bag of glyphs to be looked up by codepoint and drawn into a bitmap.
What to do about it
On 17.0 and 18.0. If you need icons newer than 2016, install base_fontawesome, and add base_fontawesome_web_editor if editors use the WYSIWYG icon picker. Both are free and readable, which as part 2 argues is reason enough to check OCA before writing anything.
On 19.0. Neither module is ported. Core’s Font Awesome 4.7.0 is what you have, and it is the last version where that sentence is true.
On 20.0 and after. Write class="oi" data-icon="…" and stop writing fa fa-*. The mapping in fa_to_ms.scss will keep old markup rendering, but its own commit calls it temporary, so treat anything it covers as a deprecation warning rather than a supported API.
Before that upgrade, count your exposure:
grep -rn "fa fa-\|'fa-\|\"fa-" --include="*.xml" --include="*.js" --include="*.scss" your_addons/
Core went from 2,754 occurrences to 90 with a dedicated task, coordinated PRs across five repositories and a 792-entry compatibility map. Custom modules get none of that machinery. Whatever that grep returns is the part of the Odoo 20 upgrade nobody has budgeted for yet, and unlike most upgrade work it is mechanical enough to start early.
The broader point is the one this article opened with. Font Awesome was filed with the fonts for a decade because of its file format, and the filing quietly implied it was a typography problem. It never was. Icons are a UI component that happened to travel in a font’s clothing, and Odoo 20 is where they finally change out of them.
-
Wikipedia entry, initial release 3 March 2012 — Font Awesome
-
Version 4 licence: font SIL OFL 1.1, code MIT, docs CC BY 3.0 — Font Awesome
-
Free licence: icons CC BY 4.0, fonts SIL OFL 1.1, code MIT — Font Awesome
-
PR #273606 — add Material Symbols icon system, remove the Font Awesome library — Odoo ↩︎
-
commit 3e15a7b — [IMP] web, *: add Material Symbols icon system (2026-04-01) — Odoo ↩︎
-
commit cda1af3 — [IMP] web, html_editor: add a subset font for Material Symbols icons — Odoo
-
commit d4113ca — [IMP] *: remove FontAwesome font-family — Odoo ↩︎
-
PR #256840 — [IMP] *: migrate to Material Symbols icon system — Odoo ↩︎
-
commit c5a40a6 — [IMP] *: apply new icon implementation globally (2026-07-09) — Odoo ↩︎
-
commit 546477dafab3 — [ADD] web: .oi, odoo ui icons (2022-01-26, Odoo 16.0) — Odoo ↩︎
-
fa_to_ms.scss — the 792-entry Font Awesome 4 to Material Symbols map — Odoo ↩︎
-
icons.scss — the .oi base class, oi-filled modifier and icon size utilities — Odoo ↩︎
-
rtl_icons.scss — directional icons mirrored under .o_rtl — Odoo ↩︎
-
web/__manifest__.py — the web.icons_fonts asset bundle — Odoo
-
mail.py — _get_icon_rendering_info and the legacy Font Awesome branch — Odoo ↩︎
-
base_fontawesome module source (18.0) — OCA server-tools ↩︎
-
base_fontawesome_web_editor module source (18.0) — OCA server-tools ↩︎