twentyhq/favicon: The Favicon Service That Treats Logos Like a Search Problem
An open-source deep dive into how a tiny NestJS service hunts through HTML clues, normalizes messy brand icons, and turns them into predictable, self-hostable assets.
- twentyhq/favicon treats logo lookup as a ranked search problem because the web scatters brand marks across tags, manifests, and fallback endpoints.
- The repo's real engineering value is normalization, not fetching, because it turns messy source assets into predictable fixed-size outputs.
- Self-hosting turns a black-box utility into infrastructure that product teams can inspect, tune, and trust.
- The service wins by choosing consistency over perfect fidelity, which is exactly what a CRM needs when it wants one stable workspace logo.
The internet does not hand you a favicon. It hands you clues.
A favicon is rarely a file. It is a scavenger hunt across HTML tags, manifest hints, legacy paths, and fallback services. `twentyhq/favicon` is interesting because it refuses the fiction that a logo lives in one obvious place.
That matters in product surfaces where the logo must be there before the user uploads anything. A CRM, an onboarding flow, or an admin console does not want a mystery box. It wants one clean asset, at a known size, every time.
Born inside Twenty, then opened up as infrastructure
The repo grew out of a practical need inside Twenty, the open-source CRM. The team wanted a reliable way to pull a company's logo from a domain, normalize it, and serve it in sizes that would not crumble in the UI. The README frames the goal set plainly: reliability, precision, quality, and open source.
We needed a solution with four main qualities: 1. Reliability to prevent downtime. 2. Precision in delivering specific icon sizes, optimizing user bandwidth. 3. High-quality resolution, offering the finest available icon for any requested size. 4. Open-source availability, as it's a universal requirement and should be accessible for contributions and hosting.
That is the right brief for a utility API. The job is not to be clever. The job is to be boring in the best possible way: predictable, inspectable, and good enough to sit under a product feature without creating support work.
Discovery is the product
The most interesting part of the repo is the discovery layer in `src/favicon/url-fetcher/`. It does not start by assuming `/favicon.ico` is the answer. It collects candidates from the page itself, including `link[rel="icon"]`, `link[rel="apple-touch-icon"]`, and `meta[itemprop="image"]`, then moves through fallback paths when the page gives up too little.
That turns the problem into ranking. The service is not asking "where is the favicon?" It is asking "which clue should win?" The answer is usually the least bad candidate, not the first candidate that happens to exist.
Normalization is where the real engineering lives
Once a candidate is found, the service has to make it useful. The pipeline uses `sharp` and `ico-to-png` to turn different source formats into a consistent PNG buffer, then resizes that buffer into a fixed set of supported sizes, including 16, 32, 64, 128, 180, and 192 pixels.
That fixed-size output is a product decision, not just a technical one. SVG could stay vector, and the service still chooses a normalized raster path because consistency is the promise. It is easier to cache, easier to serve, and less likely to surprise the consumer that expects a square image, not a shape-shifting asset.
The repo also draws a hard line with its geometry filter. It rejects assets that are not almost square, which avoids accidentally promoting a banner logo into a favicon slot. That bluntness is the point. The service is optimizing for UI reliability, not archival purity.
How it stacks up against the rest
There is a useful distinction here. Some tools generate favicon bundles for your own site. Others fetch logos for other companies. `twentyhq/favicon` belongs in the second camp, and that makes it a better fit for B2B products that need to identify a customer or prospect by domain.
| Service | Self-hostable | What it optimizes for | Output style | Best fit |
|---|---|---|---|---|
| twentyhq/favicon | Yes | Ranked discovery plus normalization | Stable PNG sizes | B2B apps and internal platforms |
| Google Favicon API | No | Fast fallback lookup | Opaque, best-effort | Quick favicon retrieval |
| Clearbit Logo API | No | Company logo enrichment | Polished but black box | SaaS onboarding and enrichment |
| Brandfetch | No | Broader brand asset coverage | Rich brand profiles | Marketing and CRM enrichment |
| FaviconKit | No | Simple favicon lookup | Utility API style | Lightweight external fetches |
The adjacent Node module `favicons` solves a different problem entirely. It generates a favicon package from your own source image. `twentyhq/favicon` is a service that goes hunting on the open web, then turns whatever it finds into something dependable.
The bigger lesson: utility APIs are becoming infrastructure you own
This repo is small, but the pattern is bigger than favicons. Teams are increasingly replacing opaque utility APIs with self-hostable services that live inside their own trust boundary. That shift matters when the feature is tiny but the operational cost of being wrong is high.
That is why this project feels more important than its size suggests. It takes a messy internet problem, imposes a clear contract, and gives product teams a service they can reason about. The code is doing infrastructure work, but the editorial insight is simpler: the most valuable API is often the one that makes uncertainty disappear.