Vue

Install Plex UI for Vue 3. Add @plexui/vue, wire up the stylesheet, and configure your router in a Vite or Nuxt app.

@plexui/vue is the Vue 3 version of Plex UI. Its components have the same props, defaults, markup and styles as the React components in @plexui/ui, and both packages read their CSS from @plexui/styles. Every component page has a Vue tab next to React.

Prerequisites

Install steps

1. Install the package

npm install @plexui/vue @plexui/styles

@plexui/styles holds the tokens, resets and component CSS modules. @plexui/vue depends on it too, but your own stylesheet and Vite config import it directly, so install it as a dependency of your app (strict installs such as pnpm do not expose a package's dependencies to the app).

2. Setup styles

Add the foundation styles and Tailwind layers to the top of your global stylesheet (e.g. main.css):

/* Pin the cascade-layer order so Plex UI's reset wins over Tailwind preflight
   for form controls. */
@layer theme, base, components, utilities;

@import "tailwindcss";
@import "@plexui/styles/css";
/* Required for Tailwind to find class references in Plex UI components. */
@source "../node_modules/@plexui/vue";

The @source path is relative to the stylesheet. The one above assumes it lives in src/.

On Vite, add the Plex UI plugin. It pins the CSS transformer and minifier so the layer order survives the build, and injects the layer order into index.html as a failsafe:

// vite.config.ts
import vue from "@vitejs/plugin-vue";
import plexui from "@plexui/styles/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [vue(), plexui()],
});

The Installation guide explains the layer order, the Lightning CSS caveats and the optional Tailwind theme import in detail. They apply to Vue unchanged; replace @plexui/ui/css with @plexui/styles/css and @plexui/ui/vite with @plexui/styles/vite.

Then import your stylesheet before mounting the app:

// main.ts
import "./main.css";
import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");

3. Server-side rendering

Components import their CSS modules from @plexui/styles, which Node cannot load on its own. When you render on the server, let your bundler process @plexui/vue instead of loading it from node_modules:

// vite.config.ts (SSR)
export default defineConfig({
  plugins: [vue(), plexui()],
  ssr: { noExternal: ["@plexui/vue", "@plexui/styles"] },
});

In Nuxt, add it to build.transpile and register the stylesheet (keep the @layer line at its top, since Nuxt does not use index.html):

// nuxt.config.ts
import plexui from "@plexui/styles/vite";

export default defineNuxtConfig({
  css: ["~/assets/css/main.css"],
  build: { transpile: ["@plexui/vue"] },
  vite: { plugins: [plexui()] },
});

Point @source at node_modules from that file: ../../node_modules/@plexui/vue for assets/css/main.css (Nuxt 3), or ../../../node_modules/@plexui/vue for app/assets/css/main.css (Nuxt 4).

4. Configure router (optional)

<PlexUIProvider> sets the default link component used by <TextLink>, <ButtonLink> and other components that render links. Pass RouterLink (or NuxtLink in Nuxt):

<script setup lang="ts">
import { RouterLink, RouterView } from "vue-router";
import { PlexUIProvider } from "@plexui/vue/components/PlexUIProvider";
</script>

<template>
  <PlexUIProvider :link-component="RouterLink">
    <RouterView />
  </PlexUIProvider>
</template>

Card, ButtonLink and TextLink pass their href to a link component that declares a to prop (such as RouterLink or NuxtLink) as to, so <TextLink href="/settings"> navigates through the router. Other link components receive href.

Start building

<script setup lang="ts">
import { ref } from "vue";
import { Badge } from "@plexui/vue/components/Badge";
import { Button } from "@plexui/vue/components/Button";
import { Input } from "@plexui/vue/components/Input";
import { Search, Sparkles } from "@plexui/vue/components/Icon";

const query = ref("");
</script>

<template>
  <div class="flex items-center gap-3">
    <Input v-model:value="query" placeholder="Search">
      <template #startAdornment><Search /></template>
    </Input>
    <Button color="primary"><Sparkles /> Generate</Button>
    <Badge color="success">New</Badge>
  </div>
</template>

From React to Vue

Component names, props and defaults are the same as in React. The differences follow Vue conventions:

PropTypeDefaultDescription
classNameclass—Classes go on the same element as in React
value + onChangev-model:value—Controlled values use a `value` prop and an `update:value` event. Other controlled props work the same way, e.g. `v-model:open` on Dialog
onX callbacks@x listeners—For example `onClick` is `@click` and `onOpenChange` is `@update:open`
ReactNode propsslots—Content props are slots, e.g. `#startAdornment`; most also accept a plain string prop
Dialog.TriggerDialogTrigger—Compound parts are flat named exports from the same module
Hookscomposables—One module per hook, e.g. `useSimulatedProgress` from `@plexui/vue/composables/useSimulatedProgress`

Icons have the same names as in React and import from @plexui/vue/components/Icon.