How to Change CSS of PrimeVue: Pass Through, Tokens, and Unstyled Mode
- Authors

- Name
- Geeks Kai
- @KaiGeeks
Loading share buttons...
Quick Answer: The best way to change CSS in PrimeVue is usually to use pass through (
pt) for component-specific styling, design tokens for theme-level styling, and unstyled mode when you want complete control. Plain CSS overrides still work, but they should not be the first tool you reach for.Best for: Vue developers who want cleaner PrimeVue customization without brittle selector overrides
Cost: Free, built into PrimeVue
Key benefit: You can style PrimeVue components at the right layer instead of fighting generated classes
PrimeVue gives you several ways to change component styles, but they are not all equally maintainable. If you only know the old pattern of overriding .p-button with stronger selectors, you will eventually run into conflicts, upgrade friction, or hard-to-read CSS.
For modern PrimeVue projects, the usual order is:
pt for component-part styling| Goal | Best approach |
|---|---|
| Change one component's internal parts | pt |
| Apply consistent theme changes across components | Design tokens / CSS variables |
| Control every class yourself | Unstyled mode |
| Patch one edge case quickly | Scoped or global CSS |
ptPrimeVue's pass through API lets you target internal DOM sections of a component directly. Each component documents the available section names, such as root, header, content, label, or icon.
This is the most useful PrimeVue-specific styling API because it lets you style component internals without guessing which selectors to override later.
Example with Panel:
<template>
<div class="card">
<Panel
header="Header"
toggleable
:pt="{
header: (options) => ({
id: 'myPanelHeader',
style: {
userSelect: 'none'
},
class: [
'border-primary',
{
'bg-primary text-primary-contrast': options.state.d_collapsed,
'text-primary bg-primary-contrast': !options.state.d_collapsed
}
]
}),
content: { class: 'border-primary text-lg text-primary-700' },
title: 'text-xl',
toggler: () => 'bg-primary text-primary-contrast hover:text-primary hover:bg-primary-contrast'
}"
>
<p class="m-0">Custom Panel content</p>
</Panel>
</div>
</template>
pt works wellclass, style, ARIA attributes, and custom attributes.p-* selectors blindlypt: SyntaxIf you prefer a more template-driven style, PrimeVue also supports declarative pass-through syntax. This is useful when the override is small and you want it close to the markup.
<Panel
pt:root:class="border border-solid"
pt:header:id="headerId"
pt:header:data-test-id="testId"
pt:header:class="bg-blue-500"
:pt:header:onClick="onHeaderClick"
>
<Button
label="Click Me"
:pt="{
root: 'bg-blue-500 text-white',
icon: 'text-white',
label: 'text-white'
}"
/>
</Panel>
Format:
<ComponentTag pt:[passthrough_key]:[attribute]="value" />
This syntax is easier to scan in simpler components, while the object form is usually better for more complex logic.
If you want consistent defaults across your app, configure pt globally when installing PrimeVue.
import PrimeVue from "primevue/config"
import { createApp } from "vue"
const app = createApp(App)
app.use(PrimeVue, {
pt: {
panel: {
header: {
class: "bg-primary text-primary-contrast",
},
},
autocomplete: {
input: {
root: "w-64",
},
},
},
})
app.mount("#app")
This is useful when:
If you want PrimeVue for functionality but not for its default visual layer, use unstyled mode.
import PrimeVue from "primevue/config"
import { createApp } from "vue"
const app = createApp(App)
app.use(PrimeVue, {
unstyled: true,
})
Unstyled mode is the best option when:
You can also combine unstyled mode with a global pt preset.
usePassThrough for Reusable PresetsIf you want reusable and mergeable configuration, PrimeVue provides usePassThrough.
import { usePassThrough } from "primevue/passthrough"
import BasePreset from "./basepreset"
const CustomPreset = usePassThrough(
BasePreset,
{
panel: {
title: {
class: ["leading-none font-light text-2xl"],
},
},
},
{
mergeSections: true,
mergeProps: false,
}
)
Then register it:
app.use(PrimeVue, { unstyled: true, pt: CustomPreset })
This is useful in larger apps where you want one shared style preset rather than repeating pt objects everywhere.
If your goal is not just one component tweak but broader visual consistency, use PrimeVue design tokens or theme variables instead of repeating local overrides.
This is usually better than CSS overrides because:
Use tokens when the change should affect multiple components, such as brand colors, spacing, or component states.
Yes. Plain CSS still works, especially for quick fixes or rare cases that are not covered by pt or tokens.
Example:
.p-button {
border-radius: 10px;
}
But plain CSS should usually be a secondary tool, not your first one.
Why:
!important!important Makes SenseMost PrimeVue guides used to recommend !important too quickly. That is usually not the best long-term choice.
Use !important only when:
ptIf you find yourself adding !important to most PrimeVue rules, it is a sign you are using the wrong customization layer.
If you are unsure which method to choose, use this order:
pt.pt config or usePassThrough.The cleanest way to change CSS in PrimeVue is not to fight its generated classes with stronger selectors. In most projects, pt is the best first choice because it targets component internals directly, works well with utility classes, and is easier to maintain.
For bigger styling systems, combine global pt config, design tokens, or unstyled mode. That gives you much more control than relying on scattered CSS overrides.