Angular Icon Library (set-agnostic) | ng-hub-ui-icons
Icon-set-agnostic Angular icons: render Font Awesome, Bootstrap Icons, Material Symbols, Solar or your own SVGs through one <hub-icon> / [hubIcon] API, themed with CSS variables.
Last updated Sep 23, 2026
Overview
Why teams search for this library
Use this Angular icon library when you want one API for any icon set — no hard dependency on a specific pack, mix sets freely, and theme them all through --hub-icon-* CSS variables.
Install
npm install ng-hub-ui-iconsJump to
Ideal for
- Use one icon API across an app that mixes Font Awesome and Material Symbols.
- Theme every icon's size and color through CSS variables, with runtime dark mode.
- Drop a <hub-icon> into any component — buttons, menus, links — by projection, with no adapter.
- Register your own SVG pack to ship brand icons through the same component.
About icons
ng-hub-ui-icons decouples how you reference an icon from which set draws it. A pack is a pure resolver from name to a render spec; the library ships factories and presets for Font Awesome, Bootstrap Icons, Material Symbols and Solar but bundles none of them. The component and directive render class-based, ligature, sprite, inline-SVG or image icons, all themed uniformly through --hub-icon-* tokens.
Feature guides
Rendering icons
Render any icon set through one API — packs, variants and the [hubIcon] directive.
Examples:
Setup
Setup
<hub-icon name="house" />pack="bi"pack="ms"name="fa:brands:github"Built-in presets — load the set's stylesheet once, then register its pack. Any other set works via a custom pack.
Font Awesome
Font Awesome
Bootstrap Icons
Bootstrap Icons
Material Symbols
Material Symbols
Solar
Solar
Custom SVG
Custom SVG
SVG sprite
SVG sprite
Image
Image
Variants
Variants
Theming & integration
Theme every pack with --hub-icon-* tokens and render icons inside other ng-hub-ui components.
Examples:
Theming
Theming
Button integration
Button integration
Key features
Recent changes
Version 22.4.0 - 9/23/26, 12:00 AM
changed: BREAKING: the Angular floor rises from 17.1.0 to 17.3.0. The old range was measured from the source alone, and its published `.d.ts` names `InputSignalWithTransform` or `OutputEmitterRef`, which Angular did not ship until 17.3. 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.3.4 - 9/23/26, 12:00 AM
changed: The Angular peer range now says what the code needs. It asked for >=21.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.3.3 - 9/21/26, 12:00 AM
fixed: The hubIcon directive builds its sprite and image nodes instead of interpolating markup. The use and img render kinds were assembled as an HTML string, so a quote inside the resolved href, src or alt closed the attribute and the rest landed in the host as markup.
Version 22.3.2 - 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.3.1 - 9/8/26, 12:00 AM
added: ng-hub-ui-ds is declared as an optional peer dependency (>=22.0.0). The whole --hub-icon-* block resolves through the family --hub-sys-* / --hub-ref-* ladder, which is how an icon follows the theme ink and its dark mode without being told to, and the manifest said nothing about it, so a consumer reading the package on npm had no way to learn which package supplies those values. It is genuinely optional: every token ends in a literal fallback and the renderer works without it.
Version 22.3.0 - 9/7/26, 12:00 AM
fixed: A colour utility on an icon does something again. <hub-icon class="text-danger"> came out in the default ink, and so did text-warning, text-success and every other utility: the library declared its own color in a rule named .hub-icon — one class, exactly what a utility is — and this stylesheet is injected at runtime, so it always lands after the sheet the application shipped. Tied on specificity, decided by source order, the primitive won. Every rule the library lands on the icon element is now written through :where(), which matches the same element and contributes zero specificity, so any declaration a consumer writes wins whatever the order — including classes this library will never hear of. Two consequences worth reading before upgrading, in BREAKING_CHANGES.md.
changed: The color input is now also written as an inline color, beside the --hub-icon-color it has always set: with the token read from a zero-specificity rule it would otherwise lose to a utility class on the same element, and an input written on one icon and no other is the more deliberate of the two.
changed: An SVG icon now takes its fill from currentColor instead of var(--hub-icon-color). The element's own color is where the token, a utility and the color input have already been resolved against each other, so reading it back keeps a drawn glyph and a filled path the same colour.
Version 22.2.0 - 9/7/26, 12:00 AM
added: label on the [hubIcon] directive, the same accessible-name input hub-icon already had: it exposes the host as role="img" with that aria-label, and without it the icon is decorative.
fixed: A screen reader no longer reads the icon's name out loud. The directive declared nothing but class="hub-icon" on its host, so every icon drawn with [hubIcon] was an unlabelled element in the accessibility tree; and with a ligature font such as Material Symbols the mechanism that draws the glyph is the icon's name written as text inside the element, so what got announced was the word "home" beside the link that already said Home. The text has to stay for the glyph to be drawn, so it is hidden rather than removed: a label-less icon is now aria-hidden="true" and a labelled one is role="img" with its aria-label. This is what hub-icon has always done; the directive had been left behind. Breaking for a host carrying its own role, aria-label or aria-hidden — see BREAKING_CHANGES.md.
Version 22.1.3 - 9/6/26, 12:00 AM
added: FUNCTIONALITIES.md, the per-feature coverage table the rest of the ecosystem ships, so a reader can see what the library does and which parts a live example actually demonstrates instead of inferring both from the list of examples.
fixed: The cssVars bridge now follows the pack that actually draws the icon. resolve() expands the pack:variant:name shorthand but the bridge lookup read the pack input alone, so an icon written with the shorthand was dressed with the default pack's custom properties, or with none, while binding the pack explicitly got the right ones. HubIconRegistry.cssVars() takes the icon name as an optional second argument so both entry points expand the same reference the same way and existing calls keep working. Latent for consumers of the shipped presets, none of which declares a bridge, and breaking for anyone who wrote a pack with one and used the documented shorthand.
fixed: The input table now describes the directive as well as the component. name was listed as required for both forms, but on [hubIcon] it is optional and falls back to the value bound to the directive, and hubIcon itself appeared in no table at all, so the only way to learn it existed was to read the source. The documentation links pointed at the site root too, leaving the reader to hunt for the icons page, and now open it directly.
fixed: The stylesheets are reachable by the subpath the README documents. The manifest declared no exports, so ng-packagr generated the minimal map and every sheet shipped in styles/ stayed outside the package's public surface. Resolvers that fall back to the filesystem found them anyway, which is why nobody noticed, while anything resolving strictly through exports answered ERR_PACKAGE_PATH_NOT_EXPORTED on the very line the docs tell you to write. The styles subpath now resolves, along with the icon, icon-base and icon-theme sheets.
Version 22.1.2 - 9/1/26, 12:00 AM
changed: The package manifest's homepage now points at this library's own documentation page instead of the site root, so the link a registry shows beside the package lands on the reference the reader was already after.
Version 22.1.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.1.0 - 7/7/26, 12:00 AM
added: hub-icon-theme(...) mixin — one-call token theming for <hub-icon> / [hubIcon]: color, size, and the variable-font axes weight / fill / grade / optical-size. Null-defaulted and additive; @use 'ng-hub-ui-icons/styles' as *;.
Version 22.0.0 - 7/1/26, 12:00 AM
added: Initial release: <hub-icon> component and [hubIcon] directive, agnostic to the icon set.
added: provideHubIcons pack registry; classPack / ligaturePack / svgPack factories and faPack / bootstrapPack / materialSymbolsPack / solarPack presets.
added: Uniform --hub-icon-* theming (size, color and variable-font axes), so one token set themes every pack identically.
Related libraries
Frequently asked questions
Does ng-hub-ui-icons include an icon set?
No, and that is the design. The package ships no icons at all — no font, no SVG files, no icon data — and depends on no icon set. It renders the set you already use: load that set's CSS or font in your application, register a pack, and <hub-icon name="house" /> resolves through it. Presets come for Font Awesome, Bootstrap Icons, Material Symbols and Solar, and classPack, ligaturePack and svgPack cover any other set.
How do I register an icon pack in Angular?
provideHubIcons({ defaultPack: 'fa', packs: { fa: faPack() } }) in the application providers. A pack is a resolver that turns a name and an optional variant into a render spec, and it can produce a CSS class glyph, a ligature, inline SVG, a reference into your own sprite or an img. name also accepts the pack:variant:name shorthand, and the pack and variant inputs win over it. With nothing registered the icon renders empty and the reason goes to your ErrorHandler rather than to the console.
What is the difference between hub-icon and hubIcon?
The component is the whole API: name, pack, variant, size, color, label and spin. The directive puts the same glyph on an element you already have and takes only name, pack, variant and label — size, colour and spinning are left to your CSS — and using it without the component means importing the stylesheet yourself with @use 'ng-hub-ui-icons/styles'. Both own role, aria-label and aria-hidden on their host, so the accessible name goes through label: an aria-label written by hand on that element is overwritten.
How do I size and recolour an icon?
By default it follows its surroundings: --hub-icon-size is 1em and --hub-icon-color is currentColor, so an icon inside a heading is heading-sized and heading-coloured with nothing set. The size and color inputs write those properties inline on the host, which beats a class on that same element, so use one approach or the other rather than both. Two caveats: a raster icon coming from an img pack ignores color, and the spin animation is fixed at one second with no token to change it.