PrimeNG
PrimeNG is one of the most comprehensive UI component libraries for Angular, maintained by PrimeTek. It ships over 90 ready-made components — feature-rich data tables, form controls, charts, overlays, menus, and file uploads — all backed by a flexible, token-based theming system. If you need a polished, dense, enterprise-style UI fast, PrimeNG gives you far more out of the box than most alternatives, with accessibility and responsiveness built in.
Installing PrimeNG
PrimeNG installs as a regular npm package alongside its companion @primeng/themes package, which provides the styled-mode theming introduced in v18. Add both, then register the theme in your application config.
npm install primeng @primeng/themes
In a standalone app, configure PrimeNG globally with providePrimeNG and pick a base theme preset such as Aura, Lara, or Nora.
import { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeng/themes/aura';
export const appConfig: ApplicationConfig = {
providers: [
provideAnimationsAsync(),
providePrimeNG({
theme: { preset: Aura },
}),
],
};
PrimeNG 18+ uses styled mode by default — themes are generated from design tokens at runtime, so you no longer import a precompiled CSS theme file. The older
primeng/resources/themes/*.cssimports are obsolete.
Importing components
PrimeNG components are standalone, so you import only the ones a component uses directly into its imports array. There is no shared NgModule to register, which keeps bundles tree-shakeable.
import { Component, signal } from '@angular/core';
import { ButtonModule } from 'primeng/button';
import { CardModule } from 'primeng/card';
@Component({
selector: 'app-welcome',
standalone: true,
imports: [ButtonModule, CardModule],
template: `
<p-card header="Welcome">
<p>You clicked {{ count() }} times.</p>
<p-button label="Click me" icon="pi pi-plus" (onClick)="increment()" />
</p-card>
`,
})
export class WelcomeComponent {
count = signal(0);
increment() {
this.count.update((n) => n + 1);
}
}
The pi pi-plus class above comes from PrimeIcons. Install it and import the stylesheet once in your global styles.css.
npm install primeicons
@import 'primeicons/primeicons.css';
Data tables
The p-table component is PrimeNG’s flagship feature: sorting, filtering, pagination, row selection, lazy loading, and column resizing are all configurable through inputs. Below, [value] accepts any array and the table renders rows with the new @for control flow inside the body template.
import { Component, signal } from '@angular/core';
import { TableModule } from 'primeng/table';
interface Product {
id: number;
name: string;
price: number;
}
@Component({
selector: 'app-products',
standalone: true,
imports: [TableModule],
template: `
<p-table
[value]="products()"
[paginator]="true"
[rows]="5"
[tableStyle]="{ 'min-width': '30rem' }"
>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="name">Name <p-sortIcon field="name" /></th>
<th pSortableColumn="price">Price <p-sortIcon field="price" /></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-product>
<tr>
<td>{{ product.name }}</td>
<td>{{ product.price | currency: 'USD' }}</td>
</tr>
</ng-template>
</p-table>
`,
})
export class ProductsComponent {
products = signal<Product[]>([
{ id: 1, name: 'Keyboard', price: 79 },
{ id: 2, name: 'Monitor', price: 249 },
{ id: 3, name: 'Mouse', price: 39 },
]);
}
Clicking the header sorts in place; the paginator footer shows page controls.
Output:
Name Price
Keyboard $79.00
Monitor $249.00
Mouse $39.00
[ < ] 1 [ > ] Showing 1 to 3 of 3 entries
Forms
PrimeNG form controls integrate with both reactive and template-driven forms. They implement ControlValueAccessor, so formControlName and [(ngModel)] work natively. Here a dropdown and input feed a reactive form.
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { InputTextModule } from 'primeng/inputtext';
import { SelectModule } from 'primeng/select';
import { ButtonModule } from 'primeng/button';
@Component({
selector: 'app-signup',
standalone: true,
imports: [ReactiveFormsModule, InputTextModule, SelectModule, ButtonModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<input pInputText formControlName="name" placeholder="Name" />
<p-select
formControlName="role"
[options]="roles"
optionLabel="label"
optionValue="value"
placeholder="Select a role"
/>
<p-button type="submit" label="Save" [disabled]="form.invalid" />
</form>
`,
})
export class SignupComponent {
private fb = inject(FormBuilder);
roles = [
{ label: 'Admin', value: 'admin' },
{ label: 'Editor', value: 'editor' },
];
form = this.fb.nonNullable.group({
name: ['', Validators.required],
role: ['', Validators.required],
});
submit() {
if (this.form.valid) console.log(this.form.getRawValue());
}
}
Common components reference
| Module | Selector | Use case |
|---|---|---|
TableModule | p-table | Data tables with sorting, paging, filtering |
SelectModule | p-select | Single-select dropdowns |
DialogModule | p-dialog | Modal dialogs |
ToastModule | p-toast | Toast notifications via MessageService |
ChartModule | p-chart | Charts powered by Chart.js |
FileUploadModule | p-fileUpload | Drag-and-drop file uploads |
CalendarModule | p-datepicker | Date and time selection |
Toasts and services
Like Angular Material, some PrimeNG features are services. MessageService powers toasts: provide it, inject it, and push messages imperatively while a single p-toast renders them.
import { Component, inject } from '@angular/core';
import { MessageService } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { ButtonModule } from 'primeng/button';
@Component({
selector: 'app-notifier',
standalone: true,
imports: [ToastModule, ButtonModule],
providers: [MessageService],
template: `
<p-toast />
<p-button label="Notify" (onClick)="notify()" />
`,
})
export class NotifierComponent {
private messages = inject(MessageService);
notify() {
this.messages.add({
severity: 'success',
summary: 'Saved',
detail: 'Your changes were stored.',
});
}
}
Theming
PrimeNG themes are built from design tokens. You can start from a preset and override tokens — colors, border radius, spacing — without touching component CSS. Dark mode is driven by a CSS selector configured in providePrimeNG.
providePrimeNG({
theme: {
preset: Aura,
options: {
darkModeSelector: '.app-dark',
},
},
});
Toggling the .app-dark class on the document root swaps the entire palette to dark.
Best practices
- Import only the specific component modules each standalone component needs so unused widgets stay tree-shaken out of the bundle.
- Configure theming through token overrides and presets rather than hand-writing CSS for individual components — it keeps dark mode and rebrands trivial.
- Provide
MessageService,ConfirmationService, andDialogServiceonce at a sensible level (often the root) and inject them where needed instead of re-providing per component. - Use
[lazy]="true"with theonLazyLoadevent onp-tablefor large datasets so paging, sorting, and filtering happen on the server. - Pair PrimeIcons (
pi pi-*) and PrimeFlex utilities with components for consistent spacing and iconography across the app. - Keep PrimeNG aligned with your Angular major version and upgrade them together, since each release targets a specific framework range.