RFC: Component-Scoped Defaults & Internal Roles
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 41k
- Forks
- 7.1k
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 11
Description
Problem Definition
Problem to solve
Vuetify provides a powerful defaults system, but several limitations appear when building complex design systems or reusable blueprints.
1. Limited targeting of internal components
Nested defaults currently affect all components of the same type within the scope.
Example:
defaults: {
VCarousel: {
VBtn: { color: 'primary' }
}
}
This affects every VBtn inside the component, even if those buttons serve different roles.
There is currently no way to target specific internal elements.
2. Growing number of ad-hoc props
Many components include highly specific props to support small customizations.
Examples include props like:
menu-elevation (https://vuetifyjs.com/en/api/v-autocomplete/#props-menu-elevation)
icon-size (https://vuetifyjs.com/en/api/v-alert/#props-icon-size)
icon-color (https://vuetifyjs.com/en/api/v-field/#props-icon-color)
This leads to:
- increasing API surface
- inconsistent patterns across components
- harder learning curve for new users
3. Awkward usage of VDefaultsProvider
Localized defaults currently require wrapping templates:
<VDefaultsProvider :defaults="{ VIcon: { color: 'success' } }">
<v-btn prepend-icon="mdi-check-circle" />
</VDefaultsProvider>
This introduces extra wrappers that:
- clutter templates
- break the visual correspondence between DOM structure and UI structure
4. Slots required for small changes
Simple changes often require full slot overrides:
<v-btn prepend-icon="mdi-check-circle">
<template #prepend>
<v-icon color="success" />
</template>
</v-btn>
This breaks encapsulation and duplicates internal logic.
Proposed solution
Summary
This RFC proposes extending Vuetify’s defaults system to allow component-scoped configuration of internal elements without requiring VDefaultsProvider, ad-hoc props, or full slot overrides.
The proposal introduces:
- Component-Scoped Defaults – allowing compound components to accept a
defaultsprop. - Internal Roles – explicit namespaced roles representing internal parts of a component.
This approach would:
- reduce the number of ad-hoc props
- improve API consistency
- simplify component customization
- improve support for design systems and blueprints
Motivation
These limitations become particularly visible when building full design systems on top of Vuetify, especially when using the Blueprints feature.
When recreating complex design systems:
- many customizations need to target specific internal parts
- developers must combine slots, wrappers, and ad-hoc props
- customization patterns become inconsistent
A structured system for internal customization would provide:
- cleaner component APIs
- predictable customization patterns
- better support for design systems
- simpler mental model for developers
Proposed Solution
This RFC proposes extending the defaults system with component-scoped defaults and internal roles.
1. Component-Scoped Defaults
Compound components would accept a new prop:
defaults
Example:
<v-btn
:defaults="{
VBtn: {
PrependIcon: { color: 'success' },
AppendIcon: { color: 'warning' }
}
}"
prepend-icon="mdi-check-circle"
append-icon="mdi-account-circle"
/>
This allows customizing internal elements without:
- wrappers
- slot overrides
- extra props
2. Internal Roles
Compound components define explicit internal roles representing structural parts of the component.
Conceptual example:
roles: {
PrependIcon: VIcon,
AppendIcon: VIcon,
Loader: VProgressCircular
}
Roles represent the component anatomy and enable granular targeting.
3. Namespaced Role Targeting
Roles are namespaced by component to avoid collisions.
Example:
VBtn.PrependIcon
VBtn.AppendIcon
Or nested syntax:
{
VBtn: {
PrependIcon: { color: 'success' }
}
}
4. Cascading Defaults
Defaults resolve using a cascading model similar to CSS:
- Component instance defaults
- Parent defaults
- Global defaults
- Internal component defaults
The closest definition takes precedence.
5. Slot Compatibility
Slots receive resolved props so developers can decide whether to use the computed defaults.
<v-btn :defaults="{ VBtn: { PrependIcon: { color: 'success' }}}">
<template #prepend="{ props }">
<v-icon v-bind="props" />
</template>
</v-btn>
Alternatives Considered
Maintaining the current system
The current approach relies on a combination of:
VDefaultsProvider- ad-hoc props
- slot overrides
While functional, it leads to growing complexity and API inconsistency.
Adding more specialized props
Another approach would be introducing additional props such as:
menu-elevation
icon-color
menu-offset
However this further increases API surface and inconsistency.
Extending only VDefaultsProvider
Improving VDefaultsProvider alone would still require wrapper components and would not address the structural targeting problem.
Related discussion
A similar idea was discussed in:
https://github.com/vuetifyjs/vuetify/pull/22313#discussion_r2513392076
This RFC expands on that direction by introducing explicit internal roles.
Technical Design
Compound components define internal roles representing structural elements.
Conceptual example:
export const VBtnRoles = {
PrependIcon: VIcon,
AppendIcon: VIcon,
Loader: VProgressCircular
}
Defaults are resolved lazily only when a role is rendered.
Pseudo implementation:
function resolveRoleDefaults(role) {
return merge(
internalDefaults[role],
globalDefaults[role],
parentDefaults[role],
componentDefaults[role]
)
}
Slots receive resolved props for full flexibility.
Adoption & Migration
This proposal follows a progressive adoption strategy.
Phase 1
Introduce:
defaultsprop in compound components
No breaking changes.
Phase 2
- Internal role definitions
- Encourage the new pattern in documentation and examples.
Phase 3
Gradually deprecate some ad-hoc props that can be replaced by role defaults.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the existing defaults system and VDefaultsProvider implementation, then read the related discussion in pull request #22313. Compare the proposed component-scoped defaults, internal roles, cascading behavior, and slot compatibility with current behavior. Done means an agreed technical design with a scoped adoption plan and clear compatibility expectations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- design, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100