When another stylesheet on the page restyles an app’s PrimeVue, PrimeReact or PrimeNG components, it is matching their class names. Every component class starts with p- (p-button, p-select, p-dialog-mask), so a rule written for those names anywhere on the page applies to the app’s components too. The most common case is a second copy of the library, for example one that a browser extension injects into the page: its .p-button rule matches the app’s buttons.

The theme has had a prefix option for a long time, and it looks like it should cover this, but it does not. The option for this case is classPrefix. It was added in @primeuix/styled 1.1.0 and works in PrimeVue 5.0.3 and later, PrimeReact 11.2.1 and later, and PrimeNG 22.1.3 and later.

prefix and classPrefix

prefix renames the CSS variables. With prefix: 'exam', the theme defines --exam-primary-color instead of --p-primary-color, and the component rules read the new names. The class names do not change: the button is still .p-button, and the rule that styles it is still written for .p-button.

On its own, prefix does not protect an app. Say an app uses prefix: 'exam' and a second copy of PrimeVue on the page uses the default prefix. The second copy’s .p-button and .p-toggleswitch rules match the first app’s components. Those rules read --p-* variables, which the first app never defined, so they resolve to nothing and the toggle switches stop rendering.

classPrefix renames the classes. It is a theme option, next to prefix:

// PrimeVue
app.use(PrimeVue, {
    theme: {
        preset: Aura,
        options: { classPrefix: 'acme' }
    }
});
// PrimeReact
<PrimeReactProvider theme={{ preset: Aura, options: { classPrefix: 'acme' } }}>
    <App />
</PrimeReactProvider>
// PrimeNG
providePrimeNG({
    theme: {
        preset: Aura,
        options: { classPrefix: 'acme' }
    }
});

A Button then renders as:

<button class="acme-button acme-component">Save</button>

and the theme writes its rules for .acme-button. A stylesheet that targets .p-button no longer matches anything in the app. To give the variables their own names as well, set both options:

options: {
    prefix: 'exam',
    classPrefix: 'exam'
}

What is renamed

Everything the library generates or renders as a component class. With classPrefix: 'acme', a PrimeVue Button with an icon renders as:

<button class="acme-button acme-component" type="button" aria-label="Save" data-pc-name="button" data-pc-id="pc0" data-pc-section="root">
    <span class="acme-button-icon acme-button-icon-left pi pi-check" data-pc-section="icon"></span>
    <span class="acme-button-label" data-pc-section="label">Save</span>
</button>

and the PrimeReact Button as:

<button data-scope="button" class="acme-button acme-component" data-styled="button" data-slot="root">Save</button>

Overlays are renamed with the rest. The Select panel appended to body is acme-select-overlay acme-component, and its enter animation classes are acme-anchored-overlay-enter-from and acme-anchored-overlay-enter-active.

The generated CSS is written for the new names, and the page has no .p-button rule at all. The button rule now reads:

.acme-button {
    display: inline-flex;
    cursor: pointer;
    user-select: none;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
    color: var(--p-button-primary-color);
    ...;
}

The variable inside it still starts with --p-, because classPrefix renames classes and nothing else; the variable prefix is prefix, covered in the next section.

The p- classes in CSS passed through the theme are renamed as well. A preset with css: '.p-button-accent { ... }' is written out as .acme-button-accent, and the same goes for the css of a component in the preset.

The style elements in the head get a suffix too, so two copies of the library with different class prefixes write to separate elements instead of overwriting each other. With the prefix above, the head holds:

<style data-primevue-style-id="primitive-variables@p-acme">...</style>
<style data-primevue-style-id="semantic-variables@p-acme">...</style>
<style data-primevue-style-id="global-variables@p-acme">...</style>
<style data-primevue-style-id="global-style@p-acme">...</style>
<style data-primevue-style-id="base@p-acme">...</style>
<style data-primevue-style-id="button-variables@p-acme">...</style>
<style data-primevue-style-id="button-style@p-acme">...</style>

The suffix has the form @<prefix>-<classPrefix>, and it is only added when classPrefix is something other than p. An app that sets only prefix keeps the ids it has today.

What is not renamed

The icon classes keep their names: p-icon, p-icon-spin and the other p-icon-* classes. The icon packages render them without knowing the prefix, so renaming them in the theme would leave the icons unsized. The list of kept names is exported from @primeuix/styled as CLASS_PREFIX_KEEP. It is marked experimental, because it may go away once the icon packages read the prefix themselves.

The app’s own classes stay exactly as written, whether they are a class or className put on a component or the value of an icon or styleClass input. Data attributes such as data-pc-name, data-pc-section and data-scope do not change either, and the variable names change only when prefix is also set.

What to update

Start with the app’s own CSS: an override written as .p-button.wide { ... } has to become .acme-button.wide { ... }. The same applies to end-to-end tests and anything else that finds elements by class.

Code that builds a selector for a library element, such as a test helper or a querySelector call, can read the prefix from the theme instead of hard coding it. Each library has the same three helpers in its utils entry, @primevue/core/utils, @primereact/core/utils and primeng/utils:

import { Theme } from '@primeuix/styled';
import { getClassPrefix, prefixSelector } from '@primevue/core/utils';

const overlay = document.querySelector(prefixSelector('.p-select-overlay', getClassPrefix(Theme)));
// with classPrefix: 'acme', the selector is '.acme-select-overlay'

prefixClasses does the same for a class string.

Which option to use

The page and the app that joins it need different options.

The page that wants to protect itself sets classPrefix, together with prefix. Then another copy of the library cannot match its classes, read its variables, or write into its style elements. One thing is still shared: the other copy’s theme sets color-scheme on :root, so if that copy uses dark mode the page’s native form controls and scrollbars follow it.

An extension or a micro frontend is on the other side: it joins someone else’s page, and it uses isolated mode. Isolated mode scopes every rule and variable to the app’s own elements, whatever the class names are, and it can be combined with classPrefix. The setup is in Running two PrimeUI apps on one page.

classPrefix is documented with the other theme options on the Styled Mode page of PrimeVue, PrimeReact and PrimeNG.