A comprehensive Angular component library for TrueNAS and related applications, featuring a powerful icon system with automatic sprite generation.
npm install @truenas/ui-componentsA comprehensive Angular component library for TrueNAS and related applications, featuring a powerful icon system with automatic sprite generation.
``bash`
npm install @truenas/ui-components
- šØ Complete UI Component Library - Pre-built Angular components with consistent styling
- š¼ļø Icon Sprite System - Automatic SVG sprite generation with tree-shaking
- š¦ 7,000+ MDI Icons - Material Design Icons built-in
- šÆ Custom Icons - Support for custom SVG icons
- š Icon Registry - Integrate any third-party icon library (Lucide, Heroicons, etc.)
- š 8 Built-in Themes - Dark mode, high contrast, and more
- āæ Accessibility - WCAG 2.1 AA compliant components
`typescript
import { TnButtonComponent, TnIconComponent } from '@truenas/ui-components';
@Component({
standalone: true,
imports: [TnButtonComponent, TnIconComponent],
template:
`
})
export class MyComponent {}
Add to your angular.json:
`json`
{
"styles": [
"node_modules/@truenas/ui-components/src/styles/themes.css"
]
}
Or import in your main styles file:
`scss`
@import '@truenas/ui-components/src/styles/themes.css';
The icon system requires the sprite to be loaded before the application renders. Add this to your main.ts or application bootstrap:
`typescript
import { provideAppInitializer } from '@angular/core';
import { TnSpriteLoaderService } from '@truenas/ui-components';
bootstrapApplication(AppComponent, {
providers: [
// ... other providers
provideAppInitializer(() => {
const spriteLoader = inject(TnSpriteLoaderService);
return spriteLoader.ensureSpriteLoaded();
}),
],
});
`
Why is this required?
- Icons are rendered synchronously for optimal performance and test reliability
- The sprite must be preloaded at app startup to avoid showing fallback text
- Without this initializer, icons will display as abbreviations (e.g., "FO" for "folder") until the sprite loads
The library includes an intelligent icon system that supports multiple icon sources:
`html
`
Available sizes: xs, sm, md (default), lg, xl
`typescript`
const menuItems: TnMenuItem[] = [
{ id: '1', label: 'Home', icon: 'home', iconLibrary: 'mdi' },
{ id: '2', label: 'Settings', icon: 'cog', iconLibrary: 'mdi' },
{ id: '3', label: 'Profile', icon: 'user', iconLibrary: 'lucide' },
];
The library includes a powerful sprite generation system that scans your application and automatically creates an optimized SVG sprite containing only the icons you use.
1. Add the sprite generation script to your package.json:
`json`
{
"scripts": {
"icons": "truenas-icons generate"
}
}
2. Run sprite generation:
`bash`
npm run icons
This will:
- Scan your templates for elementstnIconMarker()
- Detect icons marked with in TypeScriptsrc/assets/icons/sprite.svg
- Generate with only used iconssrc/assets/icons/sprite-config.json
- Create with manifest
Create truenas-icons.config.js in your project root:
`javascript
export default {
// Source directories to scan for icon usage
srcDirs: ['./src/lib', './src/app'],
// Output directory for sprite files
outputDir: './src/assets/icons',
// Optional: Directory with custom SVG icons
customIconsDir: './custom-icons'
};
`
`bashUse custom source directories
npx truenas-icons generate --src ./src,./app
$3
For icons whose names are determined at runtime, use
tnIconMarker() to ensure they're included in the sprite:`typescript
import { tnIconMarker } from '@truenas/ui-components';// In arrays or objects
const actions = [
{ name: 'Save', icon: tnIconMarker('mdi-content-save') },
{ name: 'Delete', icon: tnIconMarker('mdi-delete') }
];
// In conditional logic
const icon = isEditing
? tnIconMarker('mdi-pencil')
: tnIconMarker('mdi-eye');
// In component properties
export class MyComponent {
icon = tnIconMarker('mdi-database');
}
`$3
1. Create a directory for your custom SVG icons:
`bash
mkdir custom-icons
`2. Add SVG files (they'll be prefixed with
tn- automatically):`
custom-icons/
āāā logo.svg ā Available as "tn-logo"
āāā brand.svg ā Available as "tn-brand"
āāā custom-icon.svg ā Available as "tn-custom-icon"
`3. Configure sprite generation:
`javascript
// truenas-icons.config.js
export default {
customIconsDir: './custom-icons'
};
`4. Run sprite generation:
`bash
npm run icons
`5. Use your custom icons:
`html
`Icon Registry (Advanced)
For integrating third-party icon libraries like Lucide:
`typescript
import { TnIconRegistryService } from '@truenas/ui-components';
import * as LucideIcons from 'lucide';export function setupIcons() {
const iconRegistry = inject(TnIconRegistryService);
// Register Lucide library
iconRegistry.registerLibrary({
name: 'lucide',
resolver: (iconName: string) => {
const icon = LucideIcons[iconName];
// Convert icon data to SVG string
return svgString;
}
});
// Register individual icons
iconRegistry.registerIcon('my-logo', '');
}
`Themes
Apply themes by adding a class to your root element:
`html
`Available themes:
-
tn-dark (default)
- tn-blue
- dracula
- nord
- paper
- solarized-dark
- midnight
- high-contrastDevelopment
$3
`bash
Build the library
ng build truenas-uiGenerate icon sprite for library development
yarn iconsRun Storybook
yarn run sb
`$3
`bash
Run unit tests
yarn testRun tests with coverage
yarn test-coverageRun Storybook interaction tests
yarn test-sb
`$3
`bash
Build the library
ng build truenas-uiNavigate to dist
cd dist/truenas-uiPack for testing
npm packOr publish to npm
npm publish
`Project Structure
`
projects/truenas-ui/
āāā src/
ā āāā lib/ # Component source code
ā āāā stories/ # Storybook stories
ā āāā styles/ # Global themes and styles
ā āāā public-api.ts # Public API exports
āāā assets/
ā āāā icons/
ā āāā custom/ # Custom TrueNAS icons
ā āāā sprite.svg # Generated sprite
ā āāā sprite-config.json
āāā scripts/
ā āāā icon-sprite/ # Sprite generation system
āāā package.json
`Browser Support
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
Migration Guide
$3
Breaking Change: The icon system now requires
APP_INITIALIZER setup.Before (v0.1.x):
`typescript
// Icons worked without any special setup
bootstrapApplication(AppComponent, {
providers: [/ your providers /]
});
`After (v0.2.0+):
`typescript
import { provideAppInitializer, inject } from '@angular/core';
import { TnSpriteLoaderService } from '@truenas/ui-components';bootstrapApplication(AppComponent, {
providers: [
provideAppInitializer(() => {
const spriteLoader = inject(TnSpriteLoaderService);
return spriteLoader.ensureSpriteLoaded();
}),
// ... your other providers
]
});
`What changed:
- Icons now resolve synchronously for better performance and test reliability
- Sprite must be preloaded at app startup via
APP_INITIALIZER
- Without this setup, icons will show text fallbacks until sprite loads asynchronouslyBenefits:
- ā
Faster icon rendering (no async delay)
- ā
More reliable unit tests (no timing issues)
- ā
Cleaner code (no
whenStable()` workarounds needed)Copyright Ā© iXsystems, Inc.
- Angular CLI Documentation
- Material Design Icons
- Storybook Documentation