---
title: "Installation"
description: "Learn how to install and configure Pohon UI in your Vue application, compatible with both plain Vite and Inertia."
canonical_url: "https://vinicunca.dev/pohon/getting-started/installation/vue"
---
# Installation

> Learn how to install and configure Pohon UI in your Vue application, compatible with both plain Vite and Inertia.

> [!NOTE]
> See: /pohon/getting-started/installation/nuxt
> 
> Looking for the **Nuxt** version?

> [!TIP]
> See: https://ui.nuxt.com
> 
> Pohon UI is styled with UnoCSS. If your project uses Tailwind CSS, use **Nuxt UI** instead, which also works in plain Vue.

## Setup

### Add to a Vue project

#### Install the Pohon UI package

```bash [pnpm]
pnpm add pohon-ui unocss @vinicunca/unocss-preset
```

```bash [yarn]
yarn add pohon-ui unocss @vinicunca/unocss-preset
```

```bash [npm]
npm install pohon-ui unocss @vinicunca/unocss-preset
```

```bash [bun]
bun add pohon-ui unocss @vinicunca/unocss-preset
```

#### Add the Pohon UI Vite plugin in your `vite.config.ts`

```ts [vite.config.ts (Vite)]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui()
  ]
});
```

```ts [vite.config.ts (Laravel Inertia)]
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    laravel({
      input: ['resources/js/app.ts'],
      refresh: true
    }),
    vue({
      template: {
        transformAssetUrls: {
          base: null,
          includeAbsolute: false
        }
      }
    }),
    ui({
      router: 'inertia'
    })
  ]
});
```

```ts [vite.config.ts (AdonisJS Inertia)]
import inertia from '@adonisjs/inertia/client';
import adonisjs from '@adonisjs/vite/client';
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    adonisjs({
      entrypoints: ['inertia/app/app.ts'],
      reload: ['resources/views/**/*.edge']
    }),
    inertia(),
    vue(),
    ui({
      router: 'inertia'
    })
  ]
});
```

> [!TIP]
> 
> Pohon UI registers `unplugin-auto-import` and `unplugin-vue-components`, which will generate `auto-imports.d.ts` and `components.d.ts` type declaration files. You will likely want to gitignore these, and add them to your `tsconfig`.
> 
> ```json [tsconfig.app.json]
> {
>   "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts", "components.d.ts"]
> }
> ```
> 
> ```bash [.gitignore]
> # Auto-generated type declarations
> auto-imports.d.ts
> components.d.ts
> ```

> [!WARNING]
> 
> These declaration files are only written when Vite runs. The default `create-vue` `build` script runs `vue-tsc` in parallel with `vite build` (`run-p type-check "build-only {@}"`), so on a clean checkout type-checking can start before the files exist and fail with errors like `Cannot find name 'useToast'`. Run the type-check after the build instead so the declarations are generated first.
> 
> ```json [package.json]
> {
>   "scripts": {
>     "build": "run-s build-only type-check"
>   }
> }
> ```

> [!TIP]
> 
> Internally, Pohon UI relies on custom aliases to resolve the theme types. If you're using TypeScript, add these aliases to your `tsconfig` files to enable auto-completion in your `vite.config.ts`.
> 
> ```json [tsconfig.node.json]
> {
>   "compilerOptions": {
>     "paths": {
>       "#build/ui": [
>         "./node_modules/.pohon-ui/ui"
>       ]
>     }
>   }
> }
> ```
> 
> ```json [tsconfig.app.json]
> {
>   "compilerOptions": {
>     "paths": {
>       "#build/ui/*": [
>         "./node_modules/.pohon-ui/ui/*"
>       ]
>     }
>   }
> }
> ```

#### Use the Pohon UI Vue plugin

```ts [src/main.ts (Vite)]
import ui from 'pohon-ui/vue-plugin';
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';

const app = createApp(App);

const router = createRouter({
  routes: [],
  history: createWebHistory()
});

app.use(router);
app.use(ui);

app.mount('#app');
```

```ts [resources/js/app.ts (Laravel Inertia)]
import type { DefineComponent } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import ui from 'pohon-ui/vue-plugin';
import { createApp, h } from 'vue';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel x Pohon UI';

createInertiaApp({
  title: (title) => (title ? `${title} - ${appName}` : appName),
  resolve: (name) =>
    resolvePageComponent(
      `./pages/${name}.vue`,
      import.meta.glob<DefineComponent>('./pages/**/*.vue')
    ),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .use(ui)
      .mount(el);
  }
});
```

```ts [inertia/app/app.ts (AdonisJS Inertia)]
import type { DefineComponent } from 'vue';
import { resolvePageComponent } from '@adonisjs/inertia/helpers';
import { createInertiaApp } from '@inertiajs/vue3';
import ui from 'pohon-ui/vue-plugin';
import { createApp, h } from 'vue';

const appName = import.meta.env.VITE_APP_NAME || 'AdonisJS x Pohon UI';

createInertiaApp({
  title: (title) => (title ? `${title} - ${appName}` : appName),
  resolve: (name) =>
    resolvePageComponent(
      `../pages/${name}.vue`,
      import.meta.glob<DefineComponent>('../pages/**/*.vue')
    ),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .use(ui)
      .mount(el);
  }
});
```

#### Configure UnoCSS

```ts [uno.config.ts]
import { presetVinicunca } from '@vinicunca/unocss-preset';
import { pohonColors } from 'pohon-ui/unocss';
import { defineConfig } from 'unocss';

export default defineConfig({
  presets: [presetVinicunca()],
  theme: { colors: pohonColors },
});
```

Import `virtual:uno.css` in your Vue or Inertia entrypoint before mounting the application:

```ts [src/main.ts]
import 'virtual:uno.css';
```

#### Wrap your app with App component

```vue [src/App.vue (Vite)]
<template>
  <PApp>
    <RouterView />
  </PApp>
</template>
```

```vue [resources/js/pages/index.vue (Laravel Inertia)]
<template>
  <PApp>
    <!-- Your content goes here -->
  </PApp>
</template>
```

```vue [inertia/pages/index.vue (AdonisJS Inertia)]
<template>
  <PApp>
    <!-- Your content goes here -->
  </PApp>
</template>
```

> [!NOTE]
> See: /pohon/components/app
> 
> The `App` component sets up global config and is required for **Toast**, **Tooltip** and **programmatic overlays**.

#### Add the `isolate` class to your root container

```html [index.html (Vite)]
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Pohon UI</title>
  </head>
  <body>
    <div id="app" class="isolate"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

```blade [resources/views/app.blade.php (Laravel Inertia)]
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    @inertiaHead
    @vite('resources/js/app.ts')
  </head>
  <body>
    <div class="isolate">
        @inertia
    </div>
  </body>
</html>
```

```edge [resources/views/inertia_layout.edge (AdonisJS Inertia)]
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    @inertiaHead()
    @vite(['inertia/app/app.ts', `inertia/pages/${page.component}.vue`])
  </head>
  <body>
    @inertia({ class: 'isolate' })
  </body>
</html>
```

> [!NOTE]
> 
> This ensures styles are scoped to your app and prevents issues with overlays and stacking contexts.

### Explore working examples

See the [Nuxt playground](https://github.com/vinicunca/pohon/tree/main/playgrounds/nuxt) and [Vue playground](https://github.com/vinicunca/pohon/tree/main/playgrounds/vue) for complete Pohon UI setups.

## Options

You can customize Pohon UI by providing options in your `vite.config.ts`.

### `prefix`

Use the `prefix` option to change the prefix of the components.

- Default: `P`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      prefix: 'Nuxt'
    })
  ]
});
```

### `ui`

Use the `ui` option to provide configuration for Pohon UI. This is the Vue equivalent of the `ui` key in Nuxt's `app.config.ts`.

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      ui: {
        colors: {
          primary: 'green',
          neutral: 'slate'
        }
      }
    })
  ]
});
```

### `dts`

Use the `dts` option to enable or disable the generation of declaration files for auto-imported components and composables.

- Default: `true`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      dts: false
    })
  ]
});
```

### `icon`

Use the `icon` option to set default props for the [Icon](https://vinicunca.dev/pohon/components/icon) component (`size`, `mode`, `customize`) and to configure build-time icon bundling through `clientBundle`. Bundling of Pohon UI's own icons is enabled by default when their collection is installed. Set `clientBundle: false` to opt out.

- Default: `{}`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      icon: {
        mode: 'svg',
        clientBundle: {
          scan: true
        }
      }
    })
  ]
});
```

> [!NOTE]
> See: /pohon/getting-started/integrations/icons/vue#collections
> 
> Learn more about icon collections and client bundling in the **Icons** documentation.

### `colorMode`

Use the `colorMode` option to enable or disable the color mode integration from `@vueuse/core`.

- Default: `true`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      colorMode: false
    })
  ]
});
```

### `theme.colors`

Use the `theme.colors` option to define the dynamic color aliases used to generate components theme.

- Default: `['primary', 'secondary', 'success', 'info', 'warning', 'error']`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      theme: {
        colors: ['primary', 'error']
      }
    })
  ]
});
```

> [!NOTE]
> 
> Each alias adds a color variant to every component theme that has one, whether your app uses it or not. Trim the list to the aliases you actually use, and keep `error` if you use forms since validation styles rely on it. `primary` is always included.
> 
> The `--ui-color-<alias>-*` shades themselves are not generated: define them in your CSS (see [CSS Variables](https://vinicunca.dev/pohon/getting-started/theme/css-variables)).

> [!TIP]
> See: /pohon/getting-started/theme/design-system#color-system
> 
> Learn more about color customization and theming in the Theme section.

### `theme.transitions`

Use the `theme.transitions` option to enable or disable transitions on components.

- Default: `true`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      theme: {
        transitions: false
      }
    })
  ]
});
```

> [!NOTE]
> 
> This option adds the `transition-colors` class on components with hover or active states.

### `theme.unstyled`

Use the `theme.unstyled` option to remove all default theme classes from components, keeping only their structure and the classes you provide through `class`, `ui` or `app.config.ui`.

- Default: `false`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      theme: {
        unstyled: true
      }
    })
  ]
});
```

> [!WARNING]
> 
> This strips **structural** classes too (positioning, transitions, flex/grid), not just cosmetic ones. Layout-heavy components like `Modal`, `Drawer` or `Calendar` will need you to re-supply their layout, similar to PrimeVue's unstyled mode.

### `theme.defaultVariants`

Use the `theme.defaultVariants` option to override the default `color` and `size` variants for components.

Only defaults that are exactly `primary` or `md` are replaced, so Avatar keeps `color: 'neutral'` and Separator keeps `size: 'xs'`.

- Default: `{ color: 'primary', size: 'md' }`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      theme: {
        defaultVariants: {
          color: 'neutral',
          size: 'sm'
        }
      }
    })
  ]
});
```

### `prose`

Use the `prose` option to enable Pohon UI [`Prose` components](https://vinicunca.dev/pohon/typography) and their theme.

- Default: `false`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      prose: true
    })
  ]
});
```

### `autoImport`

Use the `autoImport` option to disable composable auto-imports or to customize [`unplugin-auto-import`](https://github.com/unplugin/unplugin-auto-import) options.

- Default: `{}`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      autoImport: false
    })
  ]
});
```

> [!NOTE]
> 
> When disabled, you can still import composables explicitly from `pohon-ui/composables`.

### `components`

Use the `components` option to disable component auto-imports or to customize [`unplugin-vue-components`](https://github.com/unplugin/unplugin-vue-components) options.

- Default: `{}`

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      components: false
    })
  ]
});
```

> [!NOTE]
> 
> When disabled, you can still import components explicitly, e.g. `import Button from 'pohon-ui/components/Button.vue'` or `import ProseCode from 'pohon-ui/components/prose/Code.vue'`.

### `router`

Use the `router` option to configure routing integration. This is useful for applications that don't use `vue-router`, such as Electron apps, MPAs, or frameworks like [Inertia.js](https://inertiajs.com/) or [Hybridly](https://hybridly.dev/).

- Default: `true`

| Value | Description |
| --- | --- |
| `true` | Uses `vue-router` for navigation with `RouterLink` component. |
| `false` | Disables routing integration, links render as plain `<a>` tags. |
| `'inertia'` | Uses Inertia.js for navigation with its `Link` component. |

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      router: false
    })
  ]
});
```

> [!TIP]
> 
> You can provide custom navigation logic for frameworks like **Hybridly** by setting `router: false` in the Vite config and passing a function when installing the Vue plugin:
> 
> ```ts [src/main.ts]
> import { router } from 'hybridly';
> import ui from 'pohon-ui/vue-plugin';
> 
> app.use(ui, {
>   router: (event, { href, external }) => {
>     if (external) {
>       return;
>     }
> 
>     event.preventDefault();
> 
>     router.navigate({ url: href });
>   }
> });
> ```

> [!NOTE]
> 
> When set to `false` or `'inertia'`, `vue-router` is not required as a dependency.

### `scanPackages`

Use the `scanPackages` option to specify additional npm packages that should be scanned for components using Pohon UI. This is useful when you have a shared component library that uses Pohon UI components internally.

```ts [vite.config.ts]
import vue from '@vitejs/plugin-vue';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    UnoCSS(),
    vue(),
    ui({
      scanPackages: ['@my-org/ui-components']
    })
  ]
});
```

> [!NOTE]
> 
> By default, only `pohon-ui` is scanned. Use this option when your external packages contain Vue components that use Pohon UI.

### `root`

Use the `root` option to override the directory where Pohon UI generates its `.pohon-ui` directory (containing the theme templates). By default it uses Vite's `root`, but in setups like [`electron-vite`](https://electron-vite.org/) the renderer's `root` points to a sub-directory (e.g. `src/renderer`), so the templates land in `src/renderer/node_modules/.pohon-ui` where UnoCSS doesn't scan them, causing theme classes like `bg-background`, `ring-border` and `divide-border` to be missing.

```ts [electron.vite.config.ts]
import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'electron-vite';
import ui from 'pohon-ui/vite';
import UnoCSS from 'unocss/vite';

export default defineConfig({
  renderer: {
    root: 'src/renderer',
    plugins: [
      vue(),
      ui({
        root: __dirname
      })
    ]
  }
});
```

> [!NOTE]
> 
> Point `root` at your project root so the generated `.pohon-ui` directory ends up in a `node_modules` that UnoCSS scans.

## Continuous releases

Pohon UI uses [pkg.pr.new](https://github.com/stackblitz-labs/pkg.pr.new) for continuous preview releases, providing developers with instant access to the latest features and bug fixes without waiting for official releases.

Automatic preview releases are created for all commits and PRs to the `main` branch. Use them by replacing your package version with the specific commit hash or PR number.

```diff [package.json]
{
  "dependencies": {
-   "pohon-ui": "^2.0.0-rc",
+   "pohon-ui": "https://pkg.pr.new/pohon-ui@4c96909",
  }
}
```

> [!NOTE]
> 
> **pkg.pr.new** will automatically comment on PRs with the installation URL, making it easy to test changes.


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
