Vue Scoped Styles: When to Use Scoped, (), and Modules
Vue’s scoped attribute keeps component CSS from leaking out, but styling child components or slots often needs
How scoped works
With <style scoped>, Vue adds a unique attribute (e.g. data-v-7ba5bd90) to the component’s root and rewrites selectors so they target only that subtree. So .title becomes .title[data-v-7ba5bd90]. That prevents your component’s styles from affecting the rest of the app.
Use scoped by default so component styles are local and you avoid global namespace clashes.
() for child components and DOM
Child components’ root gets the parent’s data attribute, but their inner DOM does not. So a selector like .parent .child-title won’t match inside the child. >>> / ::v-deep) pierces that: the selector inside :deep() is not prefixed with the scoped attribute.
Use
Example:
.parent :deep(.child-inner-class) {
color: var(--override-color);
}
() for slot content
Content passed in via slots is rendered in the parent’s template but may live in the child’s DOM. By default, scoped styles don’t apply to that content because it’s “owned” by the parent.
Use
CSS Modules
With CSS Modules (e.g. <style module>), class names are hashed and you use them via $style.className in the template. You get true local scope and no need for
Use CSS Modules when: You want maximum isolation and don’t mind using $style in the template, or you’re in a large app where scoped +
Summary
- Scoped is the default: component styles stay local; use it for most components.
() when you must style inner DOM of a child (e.g. a library); prefer the child exposing an API (prop or variable) when possible. () when you need to style slot content from the component that defines the slot. - CSS Modules when you want hashed class names and explicit usage. Use scoped,
(), and () in a consistent way so Vue styles stay predictable.