Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Vuetify Admin & Architecture Guide

Generated June 2026 from vuetifyjs/vuetify at bbffe05

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is for the engineers, architects, and maintainers who build on Vuetify or contribute to it. Where the User Guide explains how to use components, this document explains how the framework is built: its monorepo structure, the reactive composable-injection architecture, the component composition pattern, the multi-stage build pipeline, the code-generation tooling, the testing strategy, and the security and performance characteristics of the codebase.

It is derived directly from the source at version 4.1.0 (commit bbffe05). It is detailed enough that an engineer — or an AI — could reconstruct the framework's architecture from this document alone.


Table of Contents

  1. Executive Summary
  2. Repository Architecture
  3. The createVuetify Plugin: Initialization Flow
  4. Component Composition Architecture
  5. The Composables Layer
  6. Theming Internals
  7. The Build Pipeline
  8. The API Generator
  9. Configuration Reference
  10. Styling & SASS System
  11. Testing Strategy
  12. Security Analysis
  13. Performance & Bundle Strategy
  14. Development & Operations
  15. Contributing
  16. Known Issues & Improvement Opportunities
  17. Quick Reference

1. Executive Summary

Vuetify is a Vue 3 Material Design component framework distributed as the vuetify npm package (v4.1.0, MIT). It comprises 104 stable components, 10 experimental "labs" components, 8 directives, ~63 composables, 15 icon sets, 43 locales, and a CSS/SASS design system.

Architectural thesis. The framework is organized around a single idea: one plugin builds a set of reactive subsystems and provides them through Vue's dependency-injection, and every component consumes those subsystems through small, composable hooks. There is no central store and no class hierarchy — state lives in effectScope-bound reactive objects keyed by InjectionKey symbols.

Attribute Value
Package vuetify
Version 4.1.0
License MIT
Required Vue ^3.5.0
Node (tooling) >=24.11.1
Module type ESM ("type": "module")
Package manager pnpm@10.26.1 (workspace monorepo)
Language TypeScript + TSX (JSX render functions)
Build outputs lib/ (per-file ESM), dist/ (bundled UMD/ESM), type declarations

Workspace packages: the public vuetify library, the @vuetify/api-generator tooling, and vuetifyjs.com (the documentation site).


2. Repository Architecture

Vuetify is a pnpm workspace monorepo with three packages under packages/:

vuetify/  (root: @vuetify/vuetify-root, private)
├── packages/
│   ├── vuetify/            The published framework (npm: "vuetify")
│   │   ├── src/
│   │   │   ├── framework.ts        createVuetify() — the heart
│   │   │   ├── entry-bundler.ts    CDN/UMD entry (bundles all components)
│   │   │   ├── components/         104 components (VBtn, VTextField, …)
│   │   │   ├── labs/               10 experimental components
│   │   │   ├── composables/        ~63 reactive hooks (theme, display, …)
│   │   │   ├── directives/         8 directives (ripple, intersect, …)
│   │   │   ├── blueprints/         md1 / md2 / md3 preset bundles
│   │   │   ├── iconsets/           15 icon-set adapters
│   │   │   ├── locale/             43 language files + 2 adapters
│   │   │   ├── styles/             SASS: settings, tools, generic, utilities
│   │   │   ├── util/               Helpers (color, box, propsFactory, …)
│   │   │   └── icons.ts, types.ts  Public type surface
│   │   ├── build/                  Rollup configs + babel plugins
│   │   ├── dev/                    Local playground app
│   │   └── test/                   Test setup, templates, browser commands
│   ├── api-generator/      Generates the per-component API docs (ts-morph)
│   └── docs/               vuetifyjs.com (Vite + vue-router + i18n + Algolia)
└── scripts/                Repo-wide build/dev/release scripts

Package boundaries

Package Published? Role
vuetify Yes (public) The framework consumers install.
@vuetify/api-generator No (private) Reads built types, emits API metadata for the docs.
vuetifyjs.com No (private) The documentation website, consuming vuetify via workspace:*.

Note

The docs site and api-generator depend on the library via workspace:*, so they always build against the local source. This is why a change to a component's types is reflected in the generated API docs without publishing.

Module resolution

Inside packages/vuetify, the alias @/ maps to src/. This is configured three ways so every toolchain agrees: in vite.config.ts (resolve.alias), in babel.config.js (the module-resolver plugin), and in tsconfig.json (paths). The package's vite.config.ts additionally aliases bare vuetify imports to src/framework.ts so the dev playground and tests resolve to source rather than the built library.


3. The createVuetify Plugin: Initialization Flow

framework.ts exports createVuetify(options). Understanding its flow is the key to understanding the whole framework.

export function createVuetify (vuetify: VuetifyOptions = {}) {
  const { blueprint, ...rest } = vuetify
  const options = mergeDeep(blueprint, rest)   // 1. merge blueprint under user options
  const { aliases = {}, components = {}, directives = {} } = options

  const scope = effectScope()                  // 2. own reactive scope
  return scope.run(() => {
    const defaults = createDefaults(options.defaults)   // 3. build subsystems
    const display  = createDisplay(options.display, options.ssr)
    const theme    = createTheme(options.theme)
    const icons    = createIcons(options.icons)
    const locale   = createLocale(options.locale)
    const date     = createDate(options.date, locale)
    const goTo      = createGoTo(options.goTo, locale)

    function install (app: App) { /* 4. register + provide */ }
    function unmount () { scope.stop() }

    return { install, unmount, defaults, display, theme, icons, locale, date, goTo }
  })!
}

Step-by-step

  1. Blueprint merge. If a blueprint is supplied, the user's options are deep-merged over it (mergeDeep(blueprint, rest)), so explicit options always win.
  2. Effect scope. The whole framework runs inside an effectScope(), so all reactive effects (watchers, computeds) can be torn down at once via unmount().
  3. Subsystem construction. Seven factory functions build the reactive subsystems. Note the dependency order: date and goTo receive the already-built locale instance, because dates and scrolling are locale/RTL-aware.
  4. install(app) runs when app.use(vuetify) is called. It:
    • Registers each directive via app.directive(key, …).
    • Registers each component via app.component(key, …).
    • Registers aliases — clones of components under a new name (with aliasName set).
    • Installs the theme into its own child effectScope and stops it on app unmount.
    • Provides each subsystem under its InjectionKey symbol.
    • For SSR, defers the first display.update() until after mount/hydration (or hooks into Nuxt's app:suspense:resolve).
    • Adds a global mixin exposing the reactive $vuetify object (when the Options API is enabled).

Injection symbols

Each subsystem is provided under a Symbol.for(...) injection key. Components retrieve them with inject() inside their composables:

Subsystem Symbol Consumer composable
Defaults vuetify:defaults injectDefaults() / useDefaults()
Display vuetify:display useDisplay()
Theme vuetify:theme useTheme() / provideTheme()
Icons vuetify:icons useIcon()
Locale vuetify:locale useLocale() / useRtl()
Date vuetify:date-options, vuetify:date-adapter (date components)
GoTo vuetify:goto useGoTo()

Important

Because state is provided through Vue injection, provider components like <v-theme-provider>, <v-locale-provider>, and <v-defaults-provider> work by calling the matching provide* function to override the injected instance for their subtree. This is the same mechanism the root plugin uses — there is no special-casing.


4. Component Composition Architecture

Every Vuetify component follows the same pattern. Studying VBtn reveals the template all others follow.

propsFactory + make*Props mixins

Shared prop groups are defined once as factory functions (makeBorderProps, makeDensityProps, makeElevationProps, makeRoundedProps, makeSizeProps, makeTagProps, makeThemeProps, makeVariantProps, makeLocationProps, makePositionProps, makeRouterProps, makeLoaderProps, makeGroupItemProps, makeDimensionProps, makeComponentProps, …). A component composes its props by spreading them:

export const makeVBtnProps = propsFactory({
  active: { type: Boolean, default: undefined },
  block: Boolean,
  icon: [Boolean, String, Function, Object] as PropType<boolean | IconValue>,
  ripple: { type: [Boolean, Object], default: true },
  // …component-specific props…
  ...makeBorderProps(),
  ...makeComponentProps(),
  ...makeDensityProps(),
  ...makeElevationProps(),
  ...makeRoundedProps(),
  ...makeRouterProps(),
  ...makeSizeProps(),
  ...makeTagProps(),
  ...makeThemeProps(),
  ...makeVariantProps(),
  // …
}, 'VBtn')

propsFactory(props, name) returns a callable that produces the prop definitions and supports prop renaming/defaulting at call sites.

Composable hooks pair with the prop mixins

For every make*Props() there is a matching use*() composable that reads those props and returns reactive classes/styles:

const { themeClasses } = provideTheme(props)
const { densityClasses } = useDensity(props)
const { elevationClasses } = useElevation(props)
const { roundedClasses, roundedStyles } = useRounded(props)
const { sizeClasses, sizeStyles } = useSize(props)
const { colorClasses, colorStyles, variantClasses } = useVariant(props)
const { dimensionStyles } = useDimension(props)

The render function then merges these into a single class/style binding. This is why a prop like elevation, rounded, density, color, or variant behaves identically across dozens of components — the behavior lives in one composable, not in each component.

genericComponent / defineComponent + useRender

Components are declared with genericComponent() (which preserves generic slot typing) or defineComponent(), and their render logic is registered with useRender(() => <jsx/>). Render functions are written in TSX, compiled by @vuetify/babel-plugin-jsx.

export const VBtn = genericComponent<VBtnSlots>()({
  name: 'VBtn',
  props: makeVBtnProps(),
  emits: { /* … */ },
  setup (props, { slots }) {
    const { themeClasses } = provideTheme(props)
    // …compose other hooks…
    useRender(() => withDirectives(
      <props.tag class={[ 'v-btn', themeClasses.value, /* … */ ]}>{ /* … */ }</props.tag>,
      [[vRipple, props.ripple]],
    ))
  },
})

Note

Components import sibling components directly (e.g. VBtn imports VIcon, VProgressCircular, and VDefaultsProvider). The index.ts of each component folder re-exports the public surface; the package's exports map groups related components (e.g. all VList* under vuetify/components/VList).

The defaults proxy

useDefaults() (in composables/defaults.ts) wraps the raw props in a Proxy. On every prop read it resolves: explicitly-set prop → component default (defaults[name]) → global default (defaults.global) → the prop's built-in default. It also detects PascalCase keys in a component's defaults and provides them as sub-component defaults to descendants. This is what makes createVuetify({ defaults }) and <v-defaults-provider> work transparently.


5. The Composables Layer

The composables/ directory (~63 files) is the framework's true engine. They fall into groups:

Group Composables Responsibility
Framework state theme, display, defaults, locale, icons, goto, date/ The injected subsystems built by createVuetify.
Visual props border, density, dimensions, elevation, rounded, size, variant, color, position, location, tag Map shared props to classes/styles.
Behavior group, nested/, proxiedModel, form, validation, delay, focus, focusTrap, focusGroups, toggleScope Stateful interaction logic.
DOM/observers resizeObserver, intersectionObserver, mutationObserver, hydration, documentVisibility, ssrBoot Browser integration.
Data filter, virtual, list-items, selectLink, router Data tables, lists, virtual scrolling, routing.
Specialized calendar, dateFormat, mask/, hotkey/, scroll, touch, transition, loader, stack, teleport, layout Feature-specific.

Key cross-cutting composables

  • proxiedModel — implements the v-model contract: a writable computed that reads props.modelValue and emits update:modelValue, supporting prop transforms. Nearly every input component uses it.
  • group / nested — power selection across VBtnToggle, VChipGroup, VItemGroup, VTabs, VList, VTreeview, etc. nested provides pluggable strategies for active, open, and select behavior (ActiveStrategy, OpenStrategy, SelectStrategy).
  • form / validation — createForm provides a FormProvide registry. Each input registers { validate, reset, resetValidation }; the form aggregates child validity into a tri-state model (true / false / null). validateOn defaults to 'input'; fastFail short-circuits at the first failure. validate() returns { valid, errors }.
  • layout — coordinates VApp, app bars, drawers, and footers. Each layout child registers its size and position; layout computes CSS variables (--v-layout-top, etc.) so VMain and goTo's layout option can offset correctly.

6. Theming Internals

composables/theme.ts is one of the most intricate subsystems. It is worth a maintainer's full attention.

What createTheme builds

  1. Parsed options merged over genDefaults() — which defines the full light and dark palettes and a set of variables (opacities, border/shadow colors, kbd/code colors, elevation overlay settings).
  2. name — a computed that resolves 'system' to the live OS preference (via matchMedia('(prefers-color-scheme: dark)'), updated on change).
  3. computedThemes — for each theme it: merges onto the appropriate base (light/dark), generates color variations (lighten/darken shades), and computes on- colors automatically using luminance (hasLightForeground).
  4. styles — a computed string of CSS wrapped in cascade layers.

CSS architecture: cascade layers

The generated stylesheet uses @layer to control specificity deterministically:

@layer vuetify-utilities {
  @layer theme-base { :root { --v-theme-primary: 24,103,192; … } .v-theme--dark { … } }
  @layer theme-background { .bg-primary { … } }
  @layer theme-foreground { .text-primary { … } .border-primary { … } }
}

Colors are emitted as RGB component triples (--v-theme-primary: 24,103,192) so components can apply opacity with rgb(var(--v-theme-primary), 0.5). Each non-on- color also gets an -overlay-multiplier derived from its luminance, used by the elevation overlay system.

Stylesheet injection

install(app) injects the styles. If the app uses @unhead/vue or @vueuse/head (common with Nuxt), the theme registers a head entry and patches it reactively. Otherwise it upserts a <style id="vuetify-theme-stylesheet"> element directly (respecting cspNonce). On the server it renders once without watchers.

Animated transitions

change/toggle/cycle optionally animate via the View Transitions API (document.startViewTransition) with a circular clip-path reveal whose origin is set by setTransitionOrigin(event). It bails to an instant change when prefers-reduced-motion is set or the API is unavailable.

Caution

theme.global.name.value = 'x' is a deprecated assignment path (it logs a deprecation via a Proxy). Use theme.change('x') instead. This is the likely root of the upstream "inconsistent toggle() after enabling transitions" report — mixing the deprecated setter with the transition path can leave a stale _transitionOrigin.

Scoping

theme.scoped and theme.scope allow restricting theme CSS to a selector (:where(scope)) rather than :root, for embedding Vuetify inside a host app without leaking variables.


7. The Build Pipeline

The library has a multi-target build, orchestrated from packages/vuetify/package.json:

pnpm build
# = rimraf lib dist
#   && build:lib
#   && (build:dist & build:types:lib)   # concurrent
#   && build:types:bundle
Step Command Output Tool
build:lib babel src --out-dir lib … lib/ — per-file ESM, mirrors src/ Babel
build:dist rollup -c build/rollup.config.js dist/vuetify.js, .esm.js, .cjs, labs bundles Rollup
build:types:lib tsgo --emitDeclarationOnly -p tsconfig.dist.json + transform-types.js lib/**/*.d.ts TypeScript (native preview tsgo)
build:types:bundle rollup -c build/rollup.types.config.js bundled .d.ts for dist/ rollup-plugin-dts

lib/ vs dist/

  • lib/ is the primary published artifact (main/module → lib/framework.js). It is per-file ESM, which is what enables tree-shaking by the consumer's bundler. The Babel lib env applies two custom plugins: babel-plugin-add-import-extension (adds .js extensions) and build/babel-plugin-replace-import-extension (rewrites .sass/.scss imports to .css).
  • dist/ is the bundled UMD/ESM build for CDN usage (vuetify.min.js, vuetify.esm.js), produced by Rollup with Terser.

sideEffects and tree-shaking

package.json declares sideEffects: ["*.sass", "*.scss", "*.css", "*.vue"]. This tells bundlers that JS modules are side-effect-free (safe to drop if unused) while stylesheet imports must be preserved. Combined with per-file lib/ output and vite-plugin-vuetify's auto-import, only the components a consumer references end up in their bundle.

The exports map

package.json#exports defines fine-grained entry points: ., ./styles, ./styles/core|colors|utilities, ./framework, ./blueprints/*, ./components/*, ./directives/*, ./locale, ./locale/adapters/*, ./iconsets/*, ./labs/*, ./util/colors, ./date/adapters/*, and the ./dist/* bundles. Editor tooling metadata (vetur, web-types) points at generated JSON in dist/json/.


8. The API Generator

@vuetify/api-generator produces the exhaustive per-component API metadata that powers the documentation site and editor IntelliSense. It does not ship to consumers.

How it works

  1. Reads the built library (vuetify/dist/vuetify-labs.js) and its generated importMap.json to enumerate every component.
  2. Uses ts-morph to statically analyze the component/composable/directive TypeScript types and extract props, events, slots, and exposed members.
  3. Parallelizes the heavy type analysis across worker threads with Piscina (thread count clamped to roughly half the CPU cores, scaled by free memory).
  4. Merges human-written descriptions from per-locale data and reports any props missing a description.
  5. Emits API JSON plus Vetur tags/attributes and web-types for IDE support.

CLI flags (from yargs): --components <list>, --skip-directives, --skip-composables.

cd packages/api-generator
node src/index.ts --components VBtn VCard   # generate for specific components
node src/index.ts --skip-composables

Note

Because the generator reads built types, the documentation API is always a faithful reflection of the actual TypeScript signatures — there is no hand-maintained prop list that can drift from the code.


9. Configuration Reference

The VuetifyOptions interface (from framework.ts) is the complete configuration surface.

Option Type Notes
aliases Record<string, Component> Register components under alternate names.
blueprint Blueprint A preset bundle of the options below; merged under user options.
components Record<string, Component> Components to register globally.
directives Record<string, Directive> Directives to register globally.
defaults DefaultsOptions Global + per-component default props.
display DisplayOptions mobileBreakpoint, thresholds.
theme ThemeOptions See below.
icons IconOptions defaultSet, aliases, sets.
locale LocaleOptions & RtlOptions locale, fallback, messages, adapter, rtl, separators.
date DateOptions adapter, locale, formats.
goTo GoToOptions container, duration, offset, easing, layout, patterns.
ssr boolean | { clientWidth, clientHeight? } Enable SSR mode.

ThemeOptions

Field Default Notes
defaultTheme 'system' 'light'/'dark'/'system'/custom name.
themes { light, dark } Map of theme definitions (dark, colors, variables).
variations { colors: [], lighten: 0, darken: 0 } Auto-generate shades.
transition false true or { origin, duration } for animated switches.
utilities true Emit bg-/text-/border- utility classes.
stylesheetId 'vuetify-theme-stylesheet' ID of the injected <style>.
cspNonce — Nonce for the injected style element.
scope / scoped — Restrict theme CSS to a selector.
prefix 'v-' CSS variable/class prefix.

DisplayOptions

Field Default
mobileBreakpoint 'lg' (1145px)
thresholds { xs:0, sm:600, md:840, lg:1145, xl:1545, xxl:2138 }

GoToOptions

Field Default
duration 300 (ms)
easing 'easeInOutCubic'
offset 0
layout false
container document scrolling element

Build-time environment variables

These are read by the package's Vite/Vitest configs during development and CI — not at runtime by consumers:

Variable Used in Purpose
HOST, PORT vite.config.ts Dev/preview server host/port (default port 8090).
TEST vite.config.ts Switches config into test mode (disables SSR plugin).
VITE_SSR vite.config.ts Enable the SSR dev server.
BABEL_TYPES_8_BREAKING vite.config.ts Babel compat flag (defined to 'false').
TEST_TDD_ONLY, TEST_BAIL vitest.config.ts TDD/visual-test toggles.
GITHUB_ACTIONS vitest.config.ts Selects CI reporters.

Compile-time constants defined by Babel/Vite: __VUETIFY_VERSION__, __REQUIRED_VUE__, __VUE_OPTIONS_API__.


10. Styling & SASS System

src/styles/ is a structured SASS design system:

Directory Contents
settings/ All SASS variables (colors, spacing scale, component variables).
tools/ Mixins and functions.
generic/ Resets and base element styles.
utilities/ Display, elevation, pointer-events, screen-reader, spacing helpers.
elements/ Base typographic/element styling.
main.sass The aggregate entry compiled into lib/styles/main.css.

Consumers can override variables by configuring the build plugin's styles.configFile and @use 'vuetify/settings' with (...). Three SASS entry points are exposed: vuetify (global CSS), vuetify/settings (variables), and vuetify/tools (mixins).

Important

The precompiled vuetify/styles (CSS) cannot be customized. SASS customization requires the Vite/Webpack plugin so the SASS is recompiled with the consumer's overrides — this is the single most common source of "my variable override did nothing" confusion.


11. Testing Strategy

Testing runs on Vitest 4 with two projects, configured in packages/vuetify/vitest.config.ts:

Project Pattern Environment Purpose
unit **/*.spec.{ts,tsx} jsdom Fast logic/component unit tests.
browser **/*.spec.browser.{ts,tsx} Playwright (Chromium) Real-browser interaction tests.

Key points:

  • The browser project uses @vitest/browser-playwright with a fixed 1280×800 viewport, reduced motion, and clipboard permissions for deterministic runs.
  • Visual regression uses Vizzly (@vizzly-testing/vitest), configured in vizzly.config.js (1px threshold, signature on theme + device). A tdd workflow (vizzly tdd start) supports local snapshot iteration.
  • unplugin-auto-import injects Vitest globals (describe, it, expect, vi, …) into spec files so they need no imports.
  • Test scripts: pnpm test (all), test:unit, test:browser, test:coverage (Istanbul), and test:all which shards the browser suite into 5 parallel runs.
pnpm --filter vuetify test:unit          # fast feedback
pnpm --filter vuetify test:browser       # interaction tests
pnpm --filter vuetify test:coverage      # coverage report

Tip

Component test templates live in packages/vuetify/test/templates/. The generator there throws "Cannot generate examples from props without a component" if invoked without a target — pass a component when scripting story/template generation.


12. Security Analysis

Vuetify is a client-side UI library. It has no server, no authentication, no network layer, and no secret handling of its own. The relevant security surface is the DOM behavior of its components and the safety of its style injection.

Area Assessment
Secrets None in the published package. The docs site's .env.example lists its own third-party keys (Cosmic.js, EmailJS, Algolia) — these belong to the website, not the framework, and ship empty.
Style injection The theme writes a <style> element via innerHTML with framework-generated CSS only (color triples and variable names). It is not derived from user content, and it honors cspNonce for CSP compliance.
XSS Components render through Vue's template/JSX system (escaped by default). As with any UI library, passing untrusted HTML into a slot or a component that renders raw HTML is the consumer's responsibility.
Dependencies Runtime dependency surface is minimal — the public package's only true peer is Vue; date adapters and icon fonts are optional/peer. Heavy dependencies (Babel, Rollup, Playwright, ts-morph) are dev-only.
Accessibility as security-adjacent ARIA correctness is actively maintained; e.g. an upstream fix added a missing aria-label to the treeview toggle (WCAG 4.1.2).

Note

When embedding Vuetify in a page with a strict Content Security Policy, set theme.cspNonce (and the same nonce on any icon-font CSS) so the injected stylesheet is permitted. Consider theme.scoped to avoid leaking :root variables into a host app.

Caution

Treat untrusted strings rendered through components carefully. Vuetify escapes normal bindings, but components that intentionally render markup (rich text, code highlighting) will render whatever you give them — sanitize upstream.


13. Performance & Bundle Strategy

  • Tree-shaking first. Per-file lib/ ESM + sideEffects declaration + the Vite plugin's autoImport mean a real app bundles only referenced components. Importing vuetify/components wholesale is explicitly discouraged for production.
  • Reactive efficiency. Subsystems use shallowRef/toRef and a single watchEffect per system (e.g. the display system recomputes all breakpoint booleans in one effect on resize) rather than many independent watchers.
  • Virtualization. VVirtualScroll, VDataTableVirtual, and the virtual composable render only visible rows for large datasets.
  • Observer-based DOM integration. Resize/intersection/mutation observers are attached with { passive: true } listeners and torn down via onScopeDispose, avoiding leaks.
  • SSR hydration. The display subsystem can be seeded with ssr dimensions and defers its first client update to prevent hydration mismatches.
  • CSS cascade layers keep theme/utility specificity flat, avoiding !important arms races and reducing the cost of overriding styles.

Tip

For the smallest production footprint: enable the Vite plugin's autoImport, use the SVG icon set (vuetify/iconsets/mdi-svg) instead of the full icon font, and import only the locales you actually ship from vuetify/locale.


14. Development & Operations

Working on the framework

# Install (pnpm workspace)
pnpm install

# Run the component playground (packages/vuetify/dev)
pnpm --filter vuetify dev          # default http://localhost:8090

# SSR dev server
pnpm --filter vuetify dev:ssr

# Lint (type-check + eslint, concurrently)
pnpm --filter vuetify lint
pnpm --filter vuetify lint:fix

# Build the library
pnpm --filter vuetify build

Root-level orchestration scripts (scripts/):

Script Purpose
dev.js Repo-wide dev launcher (pnpm dev).
build.js Repo-wide build (pnpm build).
post-install.js Husky + Playwright Chromium install on prepare.
confirm-npm-tag.js / parse-npm-tag.js Release tag safety checks (pnpm version).
post-release-merge.js Post-release branch merge.
lint-commit-message.js / prepare-commit-message.js Conventional-commit enforcement.
tdd-run.js Visual TDD runner.

Running the docs site

pnpm --filter vuetifyjs.com dev      # default host/port from packages/docs/.env (8095)
pnpm --filter vuetifyjs.com build

The docs site is a Vite SPA using vue-router, vue-i18n, Pinia, Algolia InstantSearch, markdown-it (with many plugins), and a PWA service worker. Its .env configures Cosmic.js CMS, EmailJS, and the search API.

Release & nightly builds

Versioning uses @lerna-lite. The master, dev, and next branches publish nightly to the @vuetify/nightly npm namespace at 1200 UTC for testing.


15. Contributing

  • Toolchain: Node ≥ 24.11.1, pnpm@10.26.1. Run pnpm install at the repo root.
  • Commits: Conventional Commits, enforced by commit-message hooks and conventional-changelog-vuetify.
  • Linting: ESLint (with the local-rules plugin, eslint-plugin-vuetify, SonarJS, and Stylistic) plus vue-tsc/tsgo type-checking. --max-warnings 0 — warnings fail CI.
  • Tests: Add *.spec.ts (unit) and/or *.spec.browser.ts (browser) next to the code. Visual changes need Vizzly snapshots.
  • all-checks: pnpm all-checks runs build + lint + tests across the workspace — the pre-PR gate.
  • Custom ESLint rules live in scripts/rules/ (e.g. import sorting) — respect them or CI fails.

Important

When adding or changing a component's props, the API generator picks them up from the built types automatically, but prop descriptions are authored separately per locale. The generator reports missing descriptions — fill them in so the docs site is complete.


16. Known Issues & Improvement Opportunities

Drawn from the project's own issue tracker and observable code behavior:

Area Observation Suggested direction
Theme transitions toggle() can behave inconsistently once theme transitions are enabled (#22900). Clear _transitionOrigin deterministically after each change; discourage the deprecated theme.global.name.value setter.
VBtn sizing Numeric size does not scale font size (#16288). Document the named-size scale; consider linking font scale to numeric size.
VSelect menu-props.maxHeight dropped %/vh support (#16257); multiple + duplicate items can't deselect (#22878). Restore unit support; key selection on a stable item-value.
VDialog Percentage max-width not respected after a layout change (#22890). Re-validate percentage width handling against the layout system.
Offline PWA icons MDI font glyphs fail offline (#20227). Recommend the SVG icon set for PWAs; precache font assets.
VTreeview Keyboard support requested (#10796); a missing toggle aria-label was fixed (#22898). Continue the a11y pass — keyboard navigation across nested groups.
VCombobox Empty value for whitespace-only input (#14874). Trim/normalize free-text input before commit.

Architectural maintainability notes

  • The composable-injection design is clean but means bugs often live in a shared composable, not the reporting component. When triaging, check the relevant use* composable (e.g. a sizing bug → composables/size.ts) before the component file.
  • theme.ts concentrates significant complexity (CSS generation, layers, variations, view-transitions, head integration). It is a good candidate for focused unit tests around genCssVariables, genOnColors, and genVariations.
  • The monorepo's docs site carries its own large dependency set (CMS, search, analytics) that is unrelated to the framework; keep that boundary clear when auditing the published package's risk surface.

17. Quick Reference

Subsystem → symbol → composable

Subsystem Injection key Composable
Theme vuetify:theme useTheme()
Display vuetify:display useDisplay()
Defaults vuetify:defaults useDefaults()
Locale vuetify:locale useLocale(), useRtl()
Icons vuetify:icons useIcon()
Date vuetify:date-adapter (date components)
GoTo vuetify:goto useGoTo()

Build targets

Target Output Consumer use
lib/ (Babel, per-file ESM) lib/framework.js npm install (tree-shakeable).
dist/ (Rollup, bundled) vuetify.min.js, vuetify.esm.js CDN / <script>.
Types lib/**/*.d.ts, bundled dist/*.d.ts TypeScript consumers.

Common commands

pnpm install                        # bootstrap workspace
pnpm --filter vuetify dev           # component playground
pnpm --filter vuetify build         # build the library
pnpm --filter vuetify lint          # type-check + eslint
pnpm --filter vuetify test:unit     # unit tests (jsdom)
pnpm --filter vuetify test:browser  # browser tests (Playwright)
pnpm all-checks                     # build + lint + test (pre-PR gate)

Inventory

Asset Count
Stable components 104
Labs components 10
Directives 8
Composables ~63
Icon sets 15
Locales 43
Blueprints 3 (md1, md2, md3)