Deep dive into PrimeVue PassThrough Props
Tags: vue
Published: 2023-09-09 15:24:54

One of the recent goals of Prime UI libraries is to be unopinionated and be as flexible as possible. Pass Through props is the key to achieve that as we can combine it with the [Unstyled](https://primevue.org/unstyled/) mode to remove the default styles to implement theming with a CSS library of your choice such as Tailwind. This integrates the flexibility of Tailwind and the convenience of PrimeVue UI components.

##Basics

So what is Pass Through (**pt**)? It is a new exciting API of PrimeVue to access the internal DOM Structure of the components. Each component defines a well defined API for props and events already however PT takes it to the next level to be able to customize attributes of internal elements such as adding arbitrary attributes so you don't need to depend on the library maintainer to add those.

Each component has a special **pt** property to define an object with keys corresponding to the available DOM elements. Each value can either be a string, an object or a function that returns a string or an object to define the arbitrary properties to apply to the element such as styling, aria, data-* or custom attributes. If the value is a string or a function that returns a string, it is considered as a class definition by default and added to the class attribute of the element. Every component documentation has a dedicated section to document the available section names exposed via PT.

Most common usage of **pt** is styling and customization. The **class** and **style** properties support the exact syntax of the corresponding Vue bindings like arrays, objects and conditionals.

The default style of Lara theme is like this;

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8m1fhysfi9axk5czrlzs.png)

So let's customize it with PrimeFlex CSS, note that any similar CSS library like Tailwind can also be used. PrimeVue has a [built-in Tailwind CSS theme](https://primevue.org/tailwind) but that needs a separate article. 

```html
<Panel header="Header" toggleable
    :pt="{
        header: (options) => ({
            id: 'myPanelHeader',
            style: {
                'user-select': 'none'
            },
            class: [
                'border-primary',
                {
                    'bg-primary': options.state.d_collapsed,
                    'bg-primary-reverse': !options.state.d_collapsed
                }
            ]
        }),
        content: { class: 'border-primary text-lg text-primary-700' },
        title: 'text-xl',                                     // OR { class: 'text-xl' }
        toggler: () => 'bg-primary hover:bg-primary-reverse'  // OR { class: 'bg-primary hover:bg-primary-reverse' }
    }">
    <p class="m-0">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
</Panel>
```

The output would be;

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ib79kaxcw5itwdait0o0.png)

##Lifecycle

Lifecycle hooks of components are also exposed as pass through using the hooks property so that callback functions can be registered. Available callbacks are **onBeforeCreate**, **onCreated**, **onBeforeUpdate**, **onUpdated**, **onBeforeMount**, **onMounted**, **onBeforeUnmount** and onUnmounted. Refer to the Vue.js documentation for detailed information about lifecycle hooks.

##Global

Defines the shared pass through properties per component type. For example, with the configuration below all panel headers have the **bg-primary** style class and the all autocomplete components have a fixed width. These settings can be overriden by a particular component as components **pt** property has higher precedence over global **pt** by default. This can be customized with the **mergeSections** and **mergeProps** settings of the **ptOptions** property of a component. The usePassThrough section later in this article covers how these two properties customize the merge of a global configuration of a component and a particular component with its own pt configuration.

```js
import { createApp } from "vue";
import PrimeVue from "primevue/config";
const app = createApp(App);

app.use(PrimeVue, { 
    pt: {
        panel: {
            header: {
                class: 'bg-primary'
            }
        },
        autocomplete: {
            input: { 
                root: 'w-16rem' // OR { class: 'w-16rem' }
            }
        }
    } 
});
```

##Custom CSS

The **global** property has a css option to define custom css that belongs to a global **pt** configuration. Common use case of this feature is defining global styles and animations related to the pass through props configuration.

```js
import { createApp } from "vue";
import PrimeVue from "primevue/config";
const app = createApp(App);

app.use(PrimeVue, { 
    pt: {
        global: {
            css: `
                button {
                    padding: 2rem;
                }

                .p-ink {
                    display: block;
                    position: absolute;
                    background: rgba(255, 255, 255, 0.5);
                    border-radius: 100%;
                    transform: scale(0);
                    pointer-events: none;
                }

                .p-ink-active {
                    animation: ripple 0.4s linear;
                }

                @keyframes ripple {
                    100% {
                        opacity: 0;
                        transform: scale(2.5);
                    }
                }
            `
        }
    } 
});
```

##usePassThrough
An existing pass through configuration is customized with the usePassThrough utility. The first parameter is the object to customize, the second parameter is the customizations and the final parameter is the behavior of merging. One of the example use cases is customizing existing unstyled themes like [Tailwind](https://primevue.org/tailwind).

```js
import {createApp} from "vue";
import PrimeVue from "primevue/config";
import { usePassThrough } from "primevue/passthrough";
import Tailwind from "primevue/passthrough/tailwind";

const app = createApp(App);

const CustomTailwind = usePassThrough(
    Tailwind,
    {
        panel: {
            title: {
                class: ['leading-none font-light text-2xl']
            }
        }
    },
    {
        mergeSections: true,
        mergeProps: false
    }
);

app.use(PrimeVue, { unstyled: true, pt: CustomTailwind });
```

The **mergeSections** defines whether the sections from the main configuration gets added and the **mergeProps** controls whether to override or merge the defined props. Defaults are **true** for **mergeSections** and **false** for **mergeProps**.

```js
const CustomTailwind = usePassThrough(
    Tailwind,
    {
        panel: {
            header: 'my_panel_header'
        }
    },
    { mergeSections: true, mergeProps: false }
);

// Output: 
// panel.header.class => 'my_panel_header'
// panel.title.class => Tailwind.panel.title.class
```

```js
const CustomTailwind = usePassThrough(
    Tailwind,
    {
        panel: {
            header: 'my_panel_header'
        }
    },
    { mergeSections: true, mergeProps: true }
);

// Output: 
// panel.header.class => [Tailwind.panel.header.class, 'my_panel_header']
// panel.title.class => Tailwind.panel.title.class
```

```js
const CustomTailwind = usePassThrough(
    Tailwind,
    {
        panel: {
            header: 'my_panel_header'
        }
    },
    { mergeSections: false, mergeProps: true }
);

// Output: 
// panel.header.class => [Tailwind.panel.header.class, 'my_panel_header']
// panel.title.class => undefined
```

```js
const CustomTailwind = usePassThrough(
    Tailwind,
    {
        panel: {
            header: 'my_panel_header'
        }
    },
    { mergeSections: false, mergeProps: false }
);

// Output: 
// panel.header.class => 'my_panel_header'
// panel.title.class => undefined
```

##Wrap Up
Pass Through props opens up many possibilities like implementing design systems using the flexibility of Tailwind along with the convenience of the built-in PrimeVue UI components. This will be covered later this week with an exclusive blog entry. Stay tuned!