Componente Avatar para Angular | ng-hub-ui-avatar

Componente avatar para Angular con iniciales, imágenes fallback, badges de presencia y variables CSS para interfaces reutilizables de identidad de usuario.

Última actualización 28 sept 2026

Visión General

Por qué los equipos buscan esta librería

Elige este componente avatar para Angular cuando necesites una interfaz robusta de identidad de usuario con fallbacks, iniciales, múltiples proveedores de imagen y ganchos de estilo pensados para design systems.

Instalación

npm install ng-hub-ui-avatar

Ir a

Ideal para

  • menús de usuario
  • listas de equipo
  • feeds de actividad
  • tarjetas de perfil

Sobre avatar

ng-hub-ui-avatar ayuda a las aplicaciones Angular a mostrar usuarios de forma consistente en menús, tarjetas de perfil e interfaces colaborativas. Cubre los casos habituales como imágenes rotas, generación de iniciales, indicadores de estado y reglas compartidas de tamaño.

Guías de uso

Fuentes de avatar

Resuelve avatares a partir de perfiles sociales, imágenes personalizadas, iniciales del nombre o un valor personalizado, con fallback automático.

Ejemplos:
Perfil de Facebook

Perfil de Facebook

Gravatar

Gravatar

Perfil de GitHub

Perfil de GitHub

Fuente de imagen personalizada

Fuente de imagen personalizada

Iniciales del nombre

Iniciales del nombre

JD
Visualización de valor

Visualización de valor

75%

Sistema de fallback

Degrada con elegancia a través de cada fuente configurada hasta mostrar una representación significativa.

Ejemplos:
Sistema de fallback

Sistema de fallback

Fallback Initials

Showing GitHub source (Facebook ID invalid)

Dimensionado

Controla las dimensiones del avatar, la proporción entre texto y tamaño y cuántas iniciales se renderizan.

Ejemplos:
Tamaño personalizado

Tamaño personalizado

SD
RS
3.5rem
CS
calc(2rem + 12px)
PS
72 (pixels)
Proporción del tamaño de texto

Proporción del tamaño de texto

RD
Límite de iniciales

Límite de iniciales

JD

Estilizado

Forma, radio de esquina, colores, bordes y estilos en línea arbitrarios, todo controlado mediante inputs y variables CSS.

Ejemplos:
Avatar redondo

Avatar redondo

R
Radio de esquinas

Radio de esquinas

R
Color de fondo y de primer plano

Color de fondo y de primer plano

C
Custom BG & FG (inputs)
TD
Themed via --hub-avatar-bg-color ([autoColor]="false")
Iniciales que se leen siempre

Iniciales que se leen siempre

The automatic palette, as it paints today

Eight names, one per palette slot. Each avatar writes black or white on the colour its name hashed to, and the first ratio below it is read back from the two colours the browser actually painted. The second is what white initials would have scored on that same colour — what the component forced before 22.14.0, and what six of these eight would still be doing.

  • GH
    measuring…
  • AL
    measuring…
  • LT
    measuring…
  • BL
    measuring…
  • DK
    measuring…
  • AT
    measuring…
  • KT
    measuring…
  • AB
    measuring…

Those six are stated rather than shown on purpose: a row of white-on-yellow initials would be a page about readable labels printing six unreadable ones. An explicit fgColor does still win, contrast or not — that is how you pin the old look, and the snippet below is all it takes.

A palette the library has never seen

#ffcc00, #cccccc and #004080 — three colours the library ships no knowledge of. An application hands them to provideAvatar({ colors }) once at bootstrap; this demo scopes the same palette to its own avatars so it does not repaint the rest of the page. Either way the ink is derived from whatever colour ends up behind the text, so the guarantee travels with the background rather than with a list of approved pairings.

  • VR
    measuring…
  • IN
    measuring…
  • RP
    measuring…
Color del borde

Color del borde

B
Estilos personalizados

Estilos personalizados

S

Interactividad

Reacciona a la interacción del usuario con el evento de clic del avatar.

Ejemplos:
Manejador del evento de clic

Manejador del evento de clic

CM
Click the avatar, or focus it and press Enter…

Grupos y badge

Apila avatares en un grupo y superpón un badge en la esquina — un punto de presencia o un contador — coloreado con la paleta semántica.

Ejemplos:
Visualización de grupo de avatares

Visualización de grupo de avatares

A
B
C
+3
Badge (punto y contador)

Badge (punto y contador)

AL
GH
AT
LT
CMF
4k
JD
9+

Contenido personalizado

Proyecta cualquier icono, SVG en línea, imagen o emoji dentro del avatar. Se centra, espacia y recorta a la forma del avatar, se dimensiona de forma agnóstica (los iconos de fuente escalan, los SVG/imágenes rellenan) y se tematiza con los inputs habituales bgColor / fgColor.

Ejemplos:
Contenido personalizado (iconos, SVG)

Contenido personalizado (iconos, SVG)

🚀

Características clave

Cambios recientes

Version 22.15.1 - 9/28/26, 12:00 AM

changed: The family list in both READMEs names `ng-hub-ui-spreadsheet`. The sheet joined the family and the other lists had not caught up. Documentation only: no code, types or styles change.

Version 22.15.0 - 9/24/26, 12:00 AM

fixed: BREAKING: a size that carries units is painted as written. The size was composed by appending px to whatever the input held, so size="1.75rem" became 1.75rempx — an invalid declaration the browser drops in silence — and the avatar rendered with no box at all, while --hub-avatar-size got parseFloat's 1.75px and dragged the badge and the group overlap down with it. Any CSS length now reaches CSS intact (1.75rem, 50%, calc(2rem + 12px), var(--profile-avatar-size)), a bare number still means pixels, and a size CSS could not paint falls back to the default 50px instead of leaving the avatar sizeless. The initials divide by calc() when the unit is relative, and a remote source is asked for a pixel count instead of NaN. See BREAKING_CHANGES.md.

changed: BREAKING: the avatarSizePx getter is replaced by the avatarSize() signal, which returns { length, pixels } — the CSS length painted, and the pixels behind it when there are any. Only code reading that member off a component instance is affected; the host still exposes --hub-avatar-size.

added: resolveAvatarSize() and the AvatarSize type are exported, and a suite covers the whole input surface — number, numeric string, px, rem, em, %, calc(), clamp(), var() and plain rubbish — asserting what the avatar writes into the DOM for each.

Version 22.14.0 - 9/23/26, 12:00 AM

fixed: BREAKING: an initials avatar picks its own ink, so its text clears WCAG AA. The component painted the hashed background inline but never a matching text colour, so the initials came out white on every automatic colour, and six of the eight could not carry them: #f1c40f scored 1.66:1, #1abc9c 2.41:1, #3498db 3.15:1, #7f8c8d 3.48:1, #e74c3c 3.82:1 and #d35400 4.17:1, against the 4.5:1 AA asks of body text. The avatar now measures the colour it is about to paint and writes black or white on it, whichever reads: the same eight now score between 5.04:1 and 12.64:1, and none of the eight fails. The palette itself is untouched, so the backgrounds still look like themselves; only the text on top of them moves. Six of the eight change appearance — see BREAKING_CHANGES.md.

fixed: The guarantee is derived, not a list of eight approved pairings. The ink comes from whatever colour ends up behind the text, and the worst case over every possible background is 4.58:1, so no colour exists where both black and white fail. A palette handed to provideAvatar({ colors }), a per-avatar bgColor and a colour this library has never seen all get the same treatment. Projected content inside an avatar with a light bgColor is corrected the same way: an icon or SVG dropped into the avatar used to take the white foreground regardless of the fill it sat on.

changed: BREAKING: fgColor has no default. It was declared as '#FFF' and compared against that exact string to decide whether the consumer had set anything, so fgColor="#FFF" was read as "unset" while fgColor="#fff" was read as "set" — the same colour, two behaviours — and no avatar ever painted #FFF, because with the input untouched the component emitted no colour at all. It is now string | undefined: unset means the avatar picks the ink, and any value set is painted as asked, including one that fails contrast. Only code that reads the input's value is affected; a template binding behaves as before. See BREAKING_CHANGES.md.

added: A suite renders an avatar for every slot of the palette, reads the two colours actually painted and fails below 4.5:1 — for the built-in palette and for one supplied through AVATAR_CONFIG, so a regression cannot hide behind a list of pinned colours.

Version 22.13.0 - 9/23/26, 12:00 AM

changed: BREAKING: the Angular floor rises from 17.1.0 to 20.2.0. The old range was measured from the source alone, and its template uses `@else if (value(); as name)`, and an `as` on an `@else if` is only accepted from Angular 20.2 — the linker of 20.1 refuses it outright. An application below the new floor could install this package and then fail to build; it now gets the peer warning it should always have had. Nothing that worked stops working. See BREAKING_CHANGES.md.

Version 22.12.3 - 9/23/26, 12:00 AM

changed: The Angular peer range now says what the code needs. It asked for >=18.0.0, which nothing in this package justified: the newest Angular API the source uses is input(), which shipped in 17.1, and the compiled output carries no linker marker above it. The range is >=17.1.0, and npm run peers:floors derives and checks that floor from now on.

Version 22.12.2 - 9/20/26, 12:00 AM

changed: The npm keywords declare ng-hub-ui, the family name somebody searching for the ecosystem types. Metadata only: no code, types or styles change.

Version 22.12.1 - 9/16/26, 12:00 AM

changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.

Version 22.12.0 - 9/8/26, 12:00 AM

changed: The two exported classes carry the Hub prefix: HubAvatarComponent and HubAvatarService. A bare AvatarComponent is a name in the consumer's own namespace, not in ours, and an application with users tends to grow an avatar component of its own; the day it does, the two collide in whichever file imports both, and the only way out is an import alias on our side of the line for a name we never had the right to take. Every other class in this family is already prefixed. Nothing about the components changed: same selector, same inputs, same behaviour, same injectable. AvatarModule keeps its name on purpose, because it is already announced for removal in 23.0.0.

deprecated: AvatarComponent and AvatarService are now deprecated aliases, kept so nothing breaks today and removed in 23.0.0. Each is a re-export of the prefixed class, so a codebase importing the old name keeps compiling and keeps getting the very same class, which is pinned by a test rather than promised in prose. See BREAKING_CHANGES.md.

added: A test reads the exported surface back from the compiled module and fails on any class that ships without the Hub prefix, save the two aliases and AvatarModule. A naming rule nothing enforces is one class away from being false: the next export lands unprefixed and nobody notices until a consumer's own AvatarComponent collides with it.

added: ng-hub-ui-ds is declared as an optional peer dependency (>=22.0.0). The avatar colours and radii resolve through the family --hub-sys-* / --hub-ref-* ladder and the manifest said nothing about it, so a consumer reading the package on npm could not tell which package supplies them. It stays optional: every token ends in a literal fallback.

Version 22.11.1 - 9/8/26, 12:00 AM

deprecated: The AvatarModule deprecation now names the release that removes it: 23.0.0. "A future major version" told a reader nothing they could plan against, because in this ecosystem the major tracks Angular's and is not an API decision anyone can anticipate. 23.0.0 is the release that moves this library to Angular 23, and it is the same release the modules of ng-hub-ui-modal, -portal, -calendar, -skeleton, -stepper and -breadcrumbs are removed in. AvatarModule.forRoot() goes with the module. Nothing changes at runtime and nothing is removed yet: both keep working exactly as before, and the migration — AvatarComponent plus provideAvatar() — is written out in BREAKING_CHANGES.md.

Version 22.11.0 - 9/7/26, 12:00 AM

added: placeholder finally paints something. The input had been declared since the component was written and nothing read it, so an avatar with no source and no initials rendered an empty circle while both READMEs listed the input as reserved. It now holds the picture of last resort: shown only while nothing else is — no source resolved, none declared, or one still loading — and never as part of the fallback chain, so it cannot outrank the initials the way handing the same URL to src would. A placeholder that itself fails to load is dropped rather than retried.

Version 22.10.0 - 9/6/26, 12:00 AM

added: Source is part of the public API. The interface that types the clickOnAvatar payload lived only inside the package, so nobody could name the type of their own handler: the choice was any or a hand-copied duplicate that drifts the moment the real one changes. It is a type, so re-exporting it from the entry point costs nothing at runtime.

changed: clickOnAvatar emits Source | null, not Source. The payload was read from a cursor into the fallback list, and that cursor legitimately sits outside it: at -1 until something resolves (an avatar drawn from projected content alone never leaves it) and past the last entry once every source has failed. Clicking in either state handed the consumer undefined through an output that promised a Source, so a handler reading $event.sourceType threw. The absence is now declared instead of leaked and the click still fires; a handler typed (source: Source) has to widen. See BREAKING_CHANGES.md.

changed: The hub-avatar component runs on OnPush. It was the last component in the family still checked on every tick of the application, so a consumer who put an avatar inside an OnPush tree paid for that check and got nothing from it. What the template reads is derived from the inputs, so it marks the view on its own, which is what made the strategy safe to switch.

changed: The avatar is derived from its inputs instead of patched from SimpleChanges. The fallback chain was rebuilt by hand in ngOnChanges, one entry added or removed per changed property, and everything it produced (the resolved picture, the initials, their inline styles) was parked in mutable fields. The chain is a function of the source inputs, so it is computed from them now; the cursor that walks it is linked to it, and what gets painted derives from wherever that cursor sits. The ChangeDetectorRef went with it, because the template reads signals and those mark the view on their own, and dependencies now arrive through inject(). The render state the component used to expose as public fields is now internal. See BREAKING_CHANGES.md.

fixed: Clearing the last source input now clears the avatar. Unsetting name (or src, or any other source) dropped the source from the fallback chain and stopped there: with nothing left to resolve, nothing repainted, and the initials or the picture of the value that had just been removed stayed on screen until some other source arrived. A list rendering avatars for a selection would keep showing the person who had just been deselected.

fixed: The avatar image is named after the person, not after the URL it came from. The alt fell back to the resolved source, so a Gravatar, Facebook or custom picture without an explicit alt made a screen reader read the whole address aloud, and an image resolved asynchronously (GitHub) shipped with no alt at all or with the address a previous source had left behind. The accessible name now comes from alt when given, from name otherwise, and stays empty when there is neither, because an avatar nobody named is decorative and silence beats a URL.

fixed: The manifest now exports the stylesheet paths the documentation teaches. The tarball has always carried styles/index.scss and styles/mixins/_avatar-theme.scss, but the generated exports map listed only the package entry point, so anything under styles/ was formally private. The Angular CLI happens not to notice, since its Sass integration falls back to resolving the package root and joins the rest of the path by hand, yet every resolver that honours the map (webpack's sass-loader, dart-sass's pkg: importer) refuses @use 'ng-hub-ui-avatar/styles' outright. Declaring ./styles and ./styles/mixins/avatar-theme makes the published surface match what the README, the docs page and BREAKING_CHANGES.md tell consumers to write, and aligns the package with ng-hub-ui-ds, which already lists its stylesheet subpaths. Packaging metadata only, with no code, no types and no styles change.

Version 22.9.3 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library's own documentation page rather than at the site root. It is the link a registry shows beside the package and the one a reader clicks from it, and landing on a front page they then have to search is a worse answer than landing on the reference for the package they were already looking at. Metadata only — no code, no types, no styles change, and nothing a consumer imports is affected.

Version 22.9.2 - 8/18/26, 12:00 AM

changed: The README now says what the remote sources cost. Gravatar, GitHub and Facebook were documented as sources resolved over HTTP, which stops one sentence short of the part a consumer has to decide on: those requests leave the visitor's browser, so the third party sees their IP address, and Gravatar also receives a hash of the email address passed to it — enough to correlate a person across every site using it. Nothing changed in the code; the alternative — custom image, initials or text — never leaves your own origin.

Version 22.9.1 - 8/8/26, 12:00 AM

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

Version 22.9.0 - 7/28/26, 12:00 AM

changed: Accent resolution now imports the canonical resolveHubAccent from ng-hub-ui-utils. The private copy under src/lib/shared/resolve-hub-accent.ts has been deleted in favour of the single, tested implementation shared family-wide. Behaviour is identical (the copy had not diverged): a bareword resolves to var(--hub-sys-color-<name>, <name>), a literal colour passes through unchanged, an empty value yields null.

added: NEW peer dependency: ng-hub-ui-utils >=22.7.0. Consumers must have ng-hub-ui-utils installed alongside this library (it is where resolveHubAccent lives). Users installing via ng add ng-hub-ui get it automatically; manual installs need npm i ng-hub-ui-utils.

Version 22.8.0 - 7/28/26, 12:00 AM

added: interactive input: exposes role=button, focusability and Enter/Space activation for avatars that bind (clickOnAvatar) — the click target used to be mouse-only.

Version 22.7.0 - 7/7/26, 12:00 AM

changed: BREAKING (packaging): the SCSS ships at ng-hub-ui-avatar/styles. The theming mixin now builds to dist/avatar/styles/... instead of dist/avatar/src/lib/styles/..., so @use 'ng-hub-ui-avatar/styles' resolves; any @use that reached into src/lib/styles has to be updated.

changed: badgeColor accepts ANY colour. On top of the built-in semantic accents it now takes a registered custom accent and a literal colour (#ff0000, rgb(), oklch(), a CSS named colour): a bareword becomes var(--hub-sys-color-<name>, <name>), a literal is used as is. The single --hub-avatar-accent slot derives the rest of the family, so the built-in colours are unchanged.

changed: Internal: host bindings moved from the @HostBinding / @HostListener decorators to the host metadata object, as the Angular style guide prescribes. No public API or behaviour change.

Version 22.6.0 - 7/7/26, 12:00 AM

added: autoColor input — set [autoColor]="false" on an initials avatar to suppress the inline hash background so it can be themed via the --hub-avatar-bg-color CSS variable (no !important). Defaults to true; an explicit bgColor still wins.

Version 22.5.0 - 6/30/26, 12:00 AM

added: Standalone AvatarComponent — import it directly (no NgModule) and configure with the new provideAvatar() environment provider.

deprecated: AvatarModule / AvatarModule.forRoot() — kept for backward compatibility (now re-exporting the standalone component); use AvatarComponent + provideAvatar() instead. Removed in 23.0.0.

Version 22.3.0 - 6/26/26, 12:00 AM

changed: BREAKING: the presence-only status input is replaced by a general badge overlay. badge (bare) or [badge]="true" renders a dot, badge="4k" a labelled pill, null or absent nothing; the colour comes from the semantic badgeColor input, so presence is expressed through it (online → success, away → warning, busy → danger, offline → secondary). HubAvatarStatus is renamed HubAvatarBadgeColor and the --hub-avatar-status-* tokens --hub-avatar-badge-*. See BREAKING_CHANGES.md.

added: Semantic colour variants for the avatar and its badge, generated in one loop: a coloured-circle avatar per semantic colour (class="hub-avatar--success") and the matching badgeColor. The hub-avatar-color-variants($colors) mixin emits both in your own CSS, for the eight semantic colours or for your own map.

Preguntas frecuentes

¿Cómo se muestra un avatar con iniciales cuando no hay foto?

Instala ng-hub-ui-avatar y dale a <hub-avatar> las dos fuentes a la vez: [src] para la imagen y [name] para el nombre de la persona. El componente arma una cadena de reserva, así que cuando la imagen no carga marca esa fuente como fallida y pasa a la siguiente, que son las iniciales. autoColor deriva el fondo de un hash del nombre, así que la misma persona siempre sale del mismo color.

¿Puede traer la foto de Gravatar o de GitHub?

Sí: gravatarId, githubId y facebookId son inputs, y provideAvatar() fija el orden en que se prueban las fuentes. Conviene saber qué implica en ejecución: el navegador pide secure.gravatar.com, api.github.com o graph.facebook.com directamente, así que esos servicios ven la IP de tus visitantes y Gravatar recibe el hash del correo.

¿Cómo se muestra un grupo de avatares solapados?

Envuélvelos en un elemento con la clase hub-avatar-group. El solape es CSS puro, no un componente: --hub-avatar-group-overlap marca cuánto se monta cada avatar sobre el anterior, y --hub-avatar-group-ring-width junto con --hub-avatar-group-ring-color dibujan el anillo que los separa.

¿Qué conviene saber antes de elegirla en vez de hacerlo a mano?

Tres cosas. size acepta solo píxeles: no hay escala sm/md/lg, y el tamaño de letra, la insignia y el relleno se derivan de --hub-avatar-size, así que un número redimensiona todo. La agrupación es una clase CSS, no un componente <hub-avatar-group>. Y HttpClient tiene que estar disponible en el inyector aunque solo pintes iniciales, porque el servicio de avatar lo pide sin condiciones. Angular Material no tiene componente de avatar con el que comparar; mat-card-avatar es una directiva de estilos.