Tree-Shaking
v0 is fully tree-shakeable. Import what you need — unused code is eliminated during bundling. No configuration required.
v0 has no side effects that prevent tree-shaking. Modern bundlers (Vite, Rollup, webpack 5) handle this automatically.
Import Strategies
v0 provides two ways to import: root imports and subpath imports. Both tree-shake correctly.
Root Imports
import { createSelection, useTheme, Atom } from '@vuetify/v0'The simplest approach. Your bundler eliminates everything you don’t use. This is the recommended default for most applications.
Subpath Imports
import { createSelection } from '@vuetify/v0/composables'
import { Atom } from '@vuetify/v0/components'
import { isObject } from '@vuetify/v0/utilities'
import { IN_BROWSER } from '@vuetify/v0/constants'
import type { ID } from '@vuetify/v0/types'Subpath imports narrow the module scope, giving bundlers less work to analyze. The size difference is negligible (well under 0.1 KB gzip in current builds) but subpaths can improve build speed on large projects.
Adapter Subpaths
Composables with optional adapters have dedicated subpaths that isolate their dependencies. Prefer these over root imports when you need a hard boundary around peer-heavy entry points:
import { useDate } from '@vuetify/v0/date'
import { useFeatures } from '@vuetify/v0/features'
import { useStorage } from '@vuetify/v0/storage'
import { useTheme } from '@vuetify/v0/theme'
import { useLocale } from '@vuetify/v0/locale'
import { useLogger } from '@vuetify/v0/logger'
import { usePermissions } from '@vuetify/v0/permissions'
import { useNotifications } from '@vuetify/v0/notifications'
import { useRules } from '@vuetify/v0/rules'
import { createDataTable } from '@vuetify/v0/data-table'
import { material } from '@vuetify/v0/palettes/material/generate'Nested adapter packages (e.g. @vuetify/v0/features/adapters/flagsmith, @vuetify/v0/locale/adapters/vue-i18n) hang off these entry points. These subpaths are primarily useful for framework authors who want to guarantee that adapter peer dependencies (like date-fns or flagsmith) don’t leak into consumer bundles.
Bundle Size
All sizes measured with Vue externalized (v0 code only), minified with esbuild.
What Things Cost
| Import | Raw | Gzip |
|---|---|---|
Constants (IN_BROWSER, SUPPORTS_TOUCH) | 0.4 KB | 0.3 KB |
Utilities (isObject, mergeDeep, clamp) | 0.7 KB | 0.4 KB |
Single composable (createSelection) | 12.1 KB | 4.9 KB |
Single composable + component (SelectionRoot, SelectionItem) | 17.3 KB | 6.8 KB |
Deep chain (createStep → createSingle → createSelection → createRegistry) | 14.4 KB | 5.8 KB |
6 composables (createSelection, createSingle, createGroup, createStep, createRegistry, useStorage) | 19.0 KB | 7.4 KB |
| 4 component families (Selection, Dialog, Checkbox, Tabs) | 43.5 KB | 14.4 KB |
Everything (import *) | 297.8 KB | 81.2 KB |
A single composable is 4% of the full library. Most apps use a fraction of v0.
Composable Sharing
Composables share internal code. Adding more composables doesn’t multiply cost linearly — they reuse the same foundation layer (registry, context, utilities).
| Composables | v0 Cost (gzip) | Per-Composable |
|---|---|---|
| 1 | 4.9 KB | 4.9 KB |
| 6 | 7.4 KB | 1.2 KB |
Root vs Subpath
| Scenario | Root | Subpath | Difference |
|---|---|---|---|
| Single composable | 4.9 KB | 4.9 KB | 0.0 KB |
| 6 composables | 7.4 KB | 7.4 KB | 0.0 KB |
The difference is negligible — current builds produce byte-identical output regardless of entry point. Use whichever import style you prefer.
Dead Code Elimination
Unused imports are eliminated even without subpaths:
// Only createSelection is used — the rest is removed
import {
createSelection,
createGroup, // dead code
createStep, // dead code
useStorage, // dead code
createTokens, // dead code
} from '@vuetify/v0'
const [use, provide, ctx] = createSelection()This produces the same bundle size as importing only createSelection.
How It Works
v0’s tree-shaking relies on three mechanisms:
1. ESM Exports
v0 ships ES modules with individual exports. Bundlers can statically analyze which exports are used and eliminate the rest.
2. Side-Effect-Free Annotations
Utility functions are annotated with /*#__NO_SIDE_EFFECTS__*/ so bundlers know they’re safe to remove when unused:
/*#__NO_SIDE_EFFECTS__*/
function isObject(value: unknown): value is object {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}3. Subpath Exports
The package.json defines subpath exports↗︎ that map to individual entry points. The block below is a representative excerpt — the published map is much larger (adapters, palettes, nested data-table paths, and more). For the isolation-oriented imports that matter day to day, see Adapter Subpaths.
{
"exports": {
".": "./dist/index.mjs",
"./components": "./dist/components/index.mjs",
"./composables": "./dist/composables/index.mjs",
"./utilities": "./dist/utilities/index.mjs",
"./constants": "./dist/constants/index.mjs",
"./types": "./dist/types/index.mjs",
"./date": "./dist/date/index.mjs",
"./theme": "./dist/theme/index.mjs"
}
}Verifying Your Bundle
Use rollup-plugin-visualizer↗︎ to inspect what v0 code ends up in your bundle:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
visualizer({ open: true }),
],
})Run vite build and a treemap opens in your browser showing exactly which v0 modules are included.
No. v0 tree-shakes out of the box with Vite, Rollup, and webpack 5. No plugins, no babel transforms, no special configuration.
Either works. Root imports (from '@vuetify/v0') are simpler and recommended for most apps. Subpath imports produce virtually identical bundle size in current builds but can marginally improve build speed on large projects. Use subpaths when authoring a library that depends on v0.
v0 functions are already annotated as side-effect-free. Adding sideEffects: false to your own package.json won’t change v0’s bundle contribution — the improvement is negligible (~0 KB in our testing).
A single composable adds ~4.9 KB gzip. Constants and utilities are even lighter (0.3–0.4 KB gzip). The full library is 81.2 KB gzip, but no app should import everything.
Check for: (1) import * which pulls in everything, (2) dynamic imports that prevent static analysis, (3) older bundlers that don’t support ESM tree-shaking. Use the visualizer plugin above to diagnose.