Icons
@ng-native/icons renders the same icon sets a web app already uses, as real native shapes
instead of an innerHTML blob. An icon set from @ng-icons/heroicons, @ng-icons/lucide or any
other @ng-icons/* package is a plain object of strings of SVG markup, and provideIcons is
ordinary dependency injection - none of that changes here. What changes is rendering: on the web
@ng-icons/core's NgIcon inserts the markup with innerHTML and sizes it with CSS custom
properties; this package's NgIcon parses the same markup and commits it as react-native-svg's
native views instead. React Native has no innerHTML to insert into and no CSS engine to size
against, so there is no other way to get an SVG on screen.
import { NgIcon } from '@ng-native/icons';
<ng-icon> once NgIcon is in the component's importsSetup
Install react-native-svg alongside whichever icon sets you want, and provide the icons the same
way you would on the web:
npm install @ng-native/icons @ng-icons/core @ng-icons/heroicons
npx expo install react-native-svgimport { Component } from '@angular/core';
import { NgIcon } from '@ng-native/icons';
import { provideIcons } from '@ng-icons/core';
import { heroBookOpen, heroAcademicCap } from '@ng-icons/heroicons/outline';
@Component({
selector: 'app-root',
imports: [NgIcon],
providers: [provideIcons({ heroBookOpen, heroAcademicCap })],
template: `<ng-icon name="heroBookOpen" [size]="28" color="#ff9f0a" />`,
})
export class App {}provideIcons can be called at any level of the injector tree, and a provideIcons closer to a
component wins over one further up - the same layering ng-icons has on the web. name accepts
either spelling: the camel-cased key the icon was provided under (heroBookOpen) or the
kebab-case form (hero-book-open); both resolve to the same icon.
If a name is not found under any provideIcons in scope, the icon asks the loader from
provideNgIconLoader, if there is one, as it does on the web. The loader can return the markup as
a string, a promise or an observable, and withCaching() asks it once per name.
import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { provideNgIconLoader, withCaching } from '@ng-icons/core';
export const iconLoader = provideNgIconLoader(
(name) =>
inject(HttpClient).get(`https://example.com/icons/${name}.svg`, { responseType: 'text' }),
withCaching(),
);The HttpClient there is the one provideNativeHttpClient() from @ng-native/platform/http
provides.
If neither has the icon, it renders nothing and, in a dev build, logs a console warning naming the missing icon. There is no fallback glyph, so a typo'd name fails visibly in development and silently in production - check the console the first time an icon does not show up.
Color and stroke width
An icon set built for the web expresses two things in CSS: the currentColor a stroke or fill
resolves against, and a stroke width carried as a custom property. Both become inputs here instead:
coloris set on the icon's host, and native paints everycurrentColorbrush in the icon from it directly. Changingcolorrecolors every stroke and fill without re-parsing or rebuilding the icon's shapes.strokeWidthfills invar(--ng-icon__stroke-width, 1.5), which is how the heroicons outline set (and others built the same way) carry their line weight.
<ng-icon name="heroBookOpen" [size]="20" color="#0a84ff" [strokeWidth]="2" />color can be a token, color="var(--accent)", which resolves against the tokens in scope for the
icon.
provideNgIconsConfig({ size, color, strokeWidth }) from @ng-icons/core sets the default of each
for every icon under it. An icon's own input wins over the config.
Raw markup
Pass svg instead of name to render markup that did not come from provideIcons - fetched at
runtime, or generated by your own code:
<ng-icon [svg]="mySvgString" [size]="24" />The parser behind this is intentionally narrow: it recognizes svg, g, path, circle,
ellipse, rect, line, polyline, polygon, defs, mask, clipPath, use,
linearGradient, radialGradient, stop, text and tspan. A mask or clip-path attribute
written as url(#id) and a <use href="#id"> (or xlink:href) point at the element with that
id. Anything outside it - a <style> block, a <filter>, a <pattern> - is left out of the
drawing, with a warning in a development build that names the tag.
Markup fetched at runtime is read and drawn in time that grows with its length, even when it is
broken: a tag, a comment or a transform that never closes is given up once. Elements nested
more than 32 deep are left out, with everything inside them, and a comment left open hides the
rest of the markup, as it does in a browser. An icon nests two or three deep.
A gradient in <defs> fills or strokes any shape that references it with url(#id):
protected readonly badge =
'<svg viewBox="0 0 24 24"><defs><linearGradient id="g" x1="0" y1="0" x2="0" y2="1">' +
'<stop offset="0" stop-color="#437dfc"/><stop offset="1" stop-color="#4ad0ef"/>' +
'</linearGradient></defs><circle cx="12" cy="12" r="10" fill="url(#g)"/></svg>';A stop takes a colour written out, with stop-opacity multiplied into it. A stop written as
currentColor takes the icon's color input, and is black without one: a color from a stylesheet
reaches a currentColor fill or stroke, and not a stop. A stop whose color is a var() is left
out.
<text> draws a line of text, positioned and sized in the drawing's own units, which with a
gradient fill is a wordmark:
<ng-icon [svg]="wordmark" style="width: 96px; height: 24px" />protected readonly wordmark =
'<svg viewBox="0 0 96 24"><defs><linearGradient id="g" x1="0" y1="0" x2="0" y2="1">' +
'<stop offset="0" stop-color="#437dfc"/><stop offset="1" stop-color="#4ad0ef"/>' +
'</linearGradient></defs>' +
'<text x="0" y="18" font-size="17" font-weight="600" fill="url(#g)">Week</text></svg>';It is a drawing, not a <text> element of the app: it does not wrap, does not follow the
device's text size, and a screen reader reads the icon's accessibilityLabel rather than its
characters. Use it for a logo or a header title, and <text> from @ng-native/components for
everything else.
A drawing with more than one colour takes each from a custom property: a fill or a stroke in
the markup whose value is a var(), as an attribute or in style, reads the token in scope for
the icon and follows it, with a fallback if it needs one.
protected readonly logo =
'<svg viewBox="0 0 10 10"><path d="M0 0L5 5" stroke="var(--brand)"/></svg>';As an image
A place that takes an image and not a view, such as a tab bar item, takes an icon through
SvgImages. render(svg, size, at) draws the markup as an ng-icon in the view container at,
asks react-native-svg for it as a PNG, and takes the view away again. It answers with an image
source, a PNG data URI with the screen's scale, or null when react-native-svg is not in the build
or does not answer. Android draws nothing off screen, so at has to be on screen. The router uses
it for an SVG tab icon: see Tabs.
Accessibility
An icon with no accessibilityLabel is treated as decorative: it is left out of the accessibility
tree entirely, which is correct for an icon sitting next to a text label that already says what it
means. Set accessibilityLabel when the icon is the only thing conveying meaning - an icon-only
button, for example - and it becomes an accessibility element with accessibilityRole="image":
<ng-icon name="heroTrash" accessibilityLabel="Delete" />A static aria-label="Delete" does the same.
Sizing
Icons are square and drawn from their viewBox, so size sets both width and height in points.
It takes a number, a number as a string (size="32"), or the units a web app writes, 18px or
1.5rem.
With no size, an icon is var(--ng-icon__size, 1em), as on the web. Inside ng-icons'
<ng-icon-stack size="32px">, or anything else that sets --ng-icon__size, it takes that size.
Otherwise it is as big as the text around it. A font-size on the icon or above it sizes it, and
so does a width and height. With no font size in scope that is 16 points.
API
ng-iconcomponent@ng-native/iconsimport { NgIcon } from '@ng-native/icons';and add NgIcon to the component's importsInputs
namestring The key the icon was provided under: heroBookOpen , or hero-book-open .
svgstringMarkup, for an icon that comes from somewhere other than a provider.
sizeunknown = pointsOf(this.config.size) Points, square. Icons are drawn from a viewBox , so this is the size on screen. A static size="32" is read as the number it spells, and size="2em" against the font size in scope, as on the web. With none, the icon is 1em : as big as the text around it, and sized by a font-size on it or above it, or by a width and height.
colorstring | undefined = this.config.color What a currentColor stroke or fill paints as. It is the host's color style, so a var() resolves against the tokens in scope, and native paints it, so binding is cheap.
strokeWidthnumber | string | undefined = this.config.strokeWidth Fills in var(--ng-icon__stroke-width, …) , which is how an outline set carries its weight.
accessibilityLabelstringAbsent leaves the icon out of the accessibility tree, which is right for a decorative one.