DEV Community

Ryan John
Ryan John

Posted on • Originally published at vureact.top

How does VuReact compile Vue 3 scoped styles to React?

VuReact is a compiler toolchain for migrating from Vue to React — and for writing React with Vue 3 syntax. In this article, we will look at how Vue SFC <style scoped> blocks are compiled into React code.

Before We Start

To keep the examples easy to read, this article follows two simple conventions:

  1. All Vue and React snippets focus on core logic only, with full component wrappers and unrelated configuration omitted.
  2. The discussion assumes you are already familiar with scoped styles in Vue 3 SFCs.

Compilation Mapping

Scoped style conversion

VuReact compiles scoped styles into CSS that carries a scope identifier, then injects the corresponding data-css-{hash} attribute into matching DOM elements.

  • Vue
<!-- Counter.vue -->
<template>
  <div class="card">
    <p>Header</p>
    <p class="content">Content</p>
  </div>
  <button>Submit</button>
</template>

<style scoped>
.card {
  border: 1px solid #e5e5e5;
  border-radius: 8px;
}
.card:hover {
  background: #2a8c5e;
}
.content {
  font-size: 12px;
}
</style>
Enter fullscreen mode Exit fullscreen mode
  • Compiled React
// Counter.jsx
import './counter-abc1234.css';

function Counter() {
  return (
    <div className="card" data-css-abc1234>
      <p data-css-abc1234>Header</p>
      <p className="content" data-css-abc1234>Content</p>
    </div>
    <button data-css-abc1234>Submit</button>
  );
}
Enter fullscreen mode Exit fullscreen mode
/* counter-abc1234.css */
.card[data-css-abc1234] {
  border: 1px solid #e5e5e5;
  border-radius: 8px;
}
.card[data-css-abc1234]:hover {
  background: #2a8c5e;
}
.content[data-css-abc1234] {
  font-size: 12px;
}
Enter fullscreen mode Exit fullscreen mode

As the example shows, Vue <style scoped> blocks are compiled into CSS files with scope markers, and React DOM elements receive the matching data-css-{hash} attribute.

Scoped attribute rules

The scope attribute is not applied to every selector in the same way. Vue's scoped-style exceptions are preserved through these compilation rules:

  1. Template elements do not receive scoped attributes.
  2. Slot content does not receive scoped attributes.

Scope isolation principles

  1. CSS selector rewriting: .card becomes .card[data-css-hash].
  2. DOM attribute injection: matching elements receive the data-css-hash attribute.
  3. Style isolation: styles apply only to elements that share the same scope marker.
  4. Collision prevention: component styles remain isolated from each other.

Compilation summary

VuReact's scoped-style compilation shows a complete scoped selector transformation pipeline:

  1. :global() is unwrapped and emitted as global CSS.
  2. :deep() is split so the left side keeps the scope marker while the inner selector remains penetrative.
  3. :slotted() is simplified, with full semantic handling still in progress.
  4. Pseudo selectors such as :hover, ::before, and :not() are preserved with scope added before the pseudo selector.
  5. Nested selectors work naturally with SCSS/Less-style nesting syntax.

Supported penetrative selectors:

Selector Status Notes
:deep() Supported Scoped on the left, penetrative on the right
:global() Supported Emits global CSS without scope wrapping
:slotted() Partial De-structuring handled, full semantics still evolving

VuReact keeps Vue's scoped-style isolation semantics while still allowing flexible penetrative selectors where needed.

Related Links

Top comments (0)