Hoist's icon system provides a factory-based API for
rendering FontAwesome Pro icons throughout an application. Rather than
importing individual FA icon definitions in each file that uses them, applications use the Icon
singleton β a centralized catalog of 150+ direct icon factories and ~40 semantic aliases, all
pre-registered with the FA library in four weight variants (regular, solid, light, thin).
Icons are a core visual element across Hoist UIs β they appear in buttons, toolbars, grid columns,
menus, tabs, tree nodes, toast messages, and more. The Icon singleton standardizes access to a
curated set of FA Pro icons and provides:
- Named factory methods β 150+ direct factories (
Icon.check(),Icon.gear(),Icon.user(), etc.) plus ~40 semantic aliases - Semantic aliases β
Icon.add(),Icon.edit(),Icon.delete(),Icon.search(),Icon.save(),Icon.refresh()that delegate to specific visual icons, providing a consistent vocabulary across apps - Weight variants β Switch between regular (default), solid, light, or thin via the
prefixprop - Intent coloring β Apply
primary,success,warning, ordangerintent for consistent semantic styling - Size control β FA size values from
2xsthrough10x - FA animation props β
spin,pulse,beat,bounce,rotation,flip, and other FontAwesome props are passed through to the underlyingFontAwesomeIconcomponent - Fixed-width default β All icons receive the
fa-fw(fixed-width) andxh-iconCSS classes automatically, ensuring consistent spacing in menus, buttons, and toolbars - HTML mode β Render as raw SVG strings for non-React contexts (e.g. Highcharts tooltips)
- File-type icons β
Icon.fileIcon({filename})maps extensions to appropriate icons
icon/
βββ Icon.ts # Icon singleton with all factory methods + IconProps type
βββ XHLogo.tsx # XH corporate logo SVG component (theme-aware)
βββ index.ts # Barrel exports + FA library registration (all icon imports)
βββ impl/
βββ IconCmp.ts # React component wrapping FontAwesomeIcon
βββ IconHtml.ts # Raw SVG string renderer for asHtml mode
The index.ts barrel file is where all FontAwesome icon definitions are imported from the
@fortawesome/pro-regular-svg-icons, @fortawesome/pro-solid-svg-icons,
@fortawesome/pro-light-svg-icons, and @fortawesome/pro-thin-svg-icons packages and registered
with the FA library. This single registration point ensures every icon used by Hoist's factory
methods is available at runtime.
Each factory method on Icon delegates to Icon.icon(), which creates either an IconCmp (a
React component wrapping FA's FontAwesomeIcon) or an IconHtml (a raw SVG string), depending on
the asHtml flag.
import {Icon} from '@xh/hoist/icon';
// Named factory methods β the most common usage
Icon.check()
Icon.gear()
Icon.users()
Icon.chartLine()
// Semantic aliases β use these for common actions
Icon.add() // β Icon.plus()
Icon.edit() // β Icon.penToSquare()
Icon.delete() // β Icon.minusCircle()
Icon.search() // β Icon.magnifyingGlass()
Icon.save() // β Icon.floppyDisk()
Icon.refresh() // β Icon.arrowsRotate()
Icon.close() // β Icon.x()
Icon.download() // β Icon.arrowDownToBracket()
Icon.upload() // β Icon.arrowUpFromBracket()// Intent applies an xh-intent-{name} CSS class for semantic coloring
Icon.check({intent: 'success'})
Icon.warning({intent: 'danger'})
Icon.infoCircle({intent: 'primary'})
// Size uses FA's size scale
Icon.spinner({size: 'lg'})
Icon.gear({size: '2x'})Since IconProps extends FontAwesome's FontAwesomeIconProps, you can pass through FA animation
and transform props directly:
Icon.spinner({spin: true}) // spinning loading indicator
Icon.bullhorn({shake: true}) // attention-grabbing announcement
Icon.star({rotation: 90}) // rotated 90 degrees
Icon.warning({bounce: true}) // bouncing warningThe Spinner component (cmp/spinner/) renders an animated FA icon for use by Mask and
LoadingIndicator. The rotation animation is applied via Hoist-owned CSS (@keyframes xh-spin
on .xh-spinner) rather than FA's animation props. This ensures the spinner remains functional
when the OS-level prefers-reduced-motion preference is enabled (FA disables all its animations
in that case) and keeps performance predictable in remote desktop environments such as Citrix.
Spinner ships with several pre-registered icon choices - faSpinnerThird, faCircleNotch, and
faSpinnerScale - all available in all four weight variants. The default icon and prefix can be
configured globally via Spinner.defaults, typically set in an app's Bootstrap.ts:
import {Spinner} from '@xh/hoist/cmp/spinner';
// Override icon and/or weight globally
Spinner.defaults.iconName = 'circle-notch';
Spinner.defaults.prefix = 'far';| Default | Type | Default | Description |
|---|---|---|---|
Spinner.defaults.iconName |
IconName |
'spinner-third' |
FA icon name for the spinner |
Spinner.defaults.prefix |
HoistIconPrefix |
'fal' |
FA icon weight/prefix |
Spinner.defaults.usePng |
boolean |
false |
Fall back to animated PNG |
Per-instance overrides can be passed as props to spinner() or via LoadingIndicator's spinner
prop, which accepts either true (use defaults) or a SpinnerProps object:
loadingIndicator({
bind: myTask,
spinner: {iconName: 'circle-notch'}
})A legacy PNG fallback is retained for environments where even CSS animations may be problematic.
Set Spinner.defaults.usePng = true globally to revert to the original animated PNG behavior.
// Default prefix is 'far' (regular)
Icon.star() // regular outline
Icon.star({prefix: 'fas'}) // solid fill
Icon.star({prefix: 'fal'}) // light stroke
Icon.star({prefix: 'fat'}) // thin strokeIcon.fileIcon() maps file extensions to appropriate icons with optional type-specific CSS classes:
Icon.fileIcon({filename: 'report.pdf'}) // β filePdf with xh-file-icon-pdf
Icon.fileIcon({filename: 'data.xlsx'}) // β fileExcel with xh-file-icon-excel
Icon.fileIcon({filename: 'photo.jpg'}) // β fileImage
Icon.fileIcon({filename: 'unknown.xyz'}) // β file (generic fallback)Use asHtml: true to get a raw SVG string instead of a React element. This is needed in contexts
that build HTML strings directly, such as Highcharts tooltip formatters.
Icon.check({asHtml: true}) // returns '<svg class="..."...'Use Icon.placeholder() to create an empty element that takes up the same space as an icon. Useful
for aligning items in menus or lists where some items have icons and others don't:
menuItem({icon: Icon.check(), text: 'Option A'}),
menuItem({icon: Icon.placeholder(), text: 'Option B'}) // aligned with AApplications are strongly encouraged to create their own Icons.ts file (typically in a core/ or
common/ directory) to centralize icon usage across the app. This file serves two purposes:
- Register custom FA icons not included in Hoist's built-in set β import from the
@fortawesome/pro-*-svg-iconspackages and calllibrary.add()to make them available at runtime. (This can also be done inBootstrap.ts, but co-locating registration with the factories that use them keeps things organized.) - Define app-specific semantic factories that map domain concepts to consistent icons
This pattern ensures that domain-specific icons are used consistently throughout the app. When a concept like "loan" or "invoice" always maps to the same icon, the app's visual language becomes more coherent β and changing an icon later requires only a single edit.
// src/core/Icons.ts
import {library} from '@fortawesome/fontawesome-svg-core';
import {faFileInvoiceDollar} from '@fortawesome/pro-regular-svg-icons';
import {Icon, IconProps} from '@xh/hoist/icon';
// 1. Register custom FA icons not in Hoist's built-in set
library.add(faFileInvoiceDollar);
// 2. Define app-specific semantic factories
// Use Icon.icon() with iconName for custom-registered icons
export const invoiceIcon = (opts: IconProps = {}) =>
Icon.icon({iconName: 'file-invoice-dollar', ...opts});
// Delegate to existing Hoist factories to give them app-specific names
export const dealIcon = (opts: IconProps = {}) =>
Icon.handshake(opts);
export const dashboardIcon = (opts: IconProps = {}) =>
Icon.layout(opts);
// Factories can embed defaults for intent, size, or weight
export const approvedIcon = (opts: IconProps = {}) =>
Icon.checkCircle({intent: 'success', ...opts});
export const rejectedIcon = (opts: IconProps = {}) =>
Icon.xCircle({intent: 'danger', ...opts});App-level factories follow the same (opts?) => Icon.xxx(opts) signature as Hoist's own
factories, so they can be used interchangeably β in buttons, grid columns, menus, and anywhere
else that accepts an icon element.
| Prop | Type | Description |
|---|---|---|
iconName |
IconName |
FA icon name (e.g. 'check', 'gear'). Required for Icon.icon(), provided automatically by named factories |
prefix |
HoistIconPrefix |
Weight variant: 'far' (regular, default), 'fas' (solid), 'fal' (light), 'fat' (thin), 'fab' (brands) |
intent |
Intent |
Applies xh-intent-{intent} CSS class for semantic coloring |
title |
string |
Tooltip text rendered as SVG <title> |
size |
string |
FA size: '2xs' through '10x' |
asHtml |
boolean |
Return raw SVG string instead of React element |
className |
string |
Additional CSS class(es) |
omit |
Thunkable<boolean> |
Skip rendering this icon when true |
The Icon singleton pre-registers all its icons with the FA library. Importing individual FA icons
in application code is only needed for icons not already in Hoist's set.
// β
Do: Use the Icon singleton
import {Icon} from '@xh/hoist/icon';
Icon.check()
// β Don't: Import FA icons directly for icons Hoist already provides
import {faCheck} from '@fortawesome/pro-regular-svg-icons';When using Icon.icon() directly for custom icons, the default prefix is 'far' (regular). If
you registered a solid-only icon, you must specify prefix: 'fas':
// β
Do: Match the prefix to how you registered the icon
Icon.icon({iconName: 'custom-icon', prefix: 'fas'})
// β Don't: Assume regular prefix when you registered a solid icon β renders blank
Icon.icon({iconName: 'custom-icon'})Always use FontAwesome icons via Hoist's Icon singleton or the app-level Icons.ts pattern.
Do not pull icons from other libraries (e.g. Blueprint icons, Material icons) unless the app has
an explicit directive to do so. Mixing icon libraries breaks the cohesive visual language that FA
provides, and FontAwesome Pro's catalog is extensive enough to cover virtually any use case. If
you can't find the right icon in Hoist's pre-registered set, register a custom one from the FA Pro
packages β don't reach for a different library.
FontAwesome updates frequently and adds new icons with each release. When browsing the FA site to
find an icon for your app, use the version picker to filter results to the version Hoist currently
depends on (check @fortawesome/pro-regular-svg-icons in package.json). Attempting to import an
icon that only exists in a newer FA version will fail at build time. Hoist endeavors to keep its FA
dependency up to date, but always verify the version before spending time wiring up a new icon.
The 'fab' (brands) prefix is supported but requires a separate import of
@fortawesome/free-brands-svg-icons in your app's bootstrap code. Brand icons are not bundled
with Hoist by default.
/cmp/spinner/β Spinner component renders an animated FA icon, configurable via static defaults on theSpinnerclass/desktop/β Desktop components use icons extensively in buttons, toolbars, menus, and grid columns