Skip to content
Angular ng libraries 5 min read

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/*.css imports 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

ModuleSelectorUse case
TableModulep-tableData tables with sorting, paging, filtering
SelectModulep-selectSingle-select dropdowns
DialogModulep-dialogModal dialogs
ToastModulep-toastToast notifications via MessageService
ChartModulep-chartCharts powered by Chart.js
FileUploadModulep-fileUploadDrag-and-drop file uploads
CalendarModulep-datepickerDate 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, and DialogService once at a sensible level (often the root) and inject them where needed instead of re-providing per component.
  • Use [lazy]="true" with the onLazyLoad event on p-table for 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.
Last updated June 14, 2026
Was this helpful?