Implementing Vue‑Style Directives in React with a Babel Plugin
This article explores three approaches to bring Vue‑like directives such as r‑if and r‑for into React, compares their pros and cons, and provides a complete Babel plugin implementation that transforms custom JSX attributes into standard React conditional and list rendering syntax.
Introduction
As a developer who works with both Vue and React, I was fascinated by Vue's directive system (v‑if, v‑for, v‑show) and wondered whether a similar system could be created for React. After several experiments I identified three possible solutions, with the Babel‑plugin approach offering the most complete result.
Why React Directives?
Traditional React conditional rendering relies on logical && and map for lists, which can be verbose. An ideal syntax would look like <div r-if={isShow}>Display content</div> and <div r-for={item in items} key={item.id}>{item.name}</div> , making templates concise and expressive.
Solution Comparison
Solution 1: Higher‑Order Components (Imperfect)
const If = ({ condition, children }) => condition ? children : null;
const For = ({ list, children }) => list.map((item, index) => children(item, index));
显示内容
{(item, index) =>
{item}
}Pros: Follows React's design philosophy and requires no extra tooling.
Cons: syntax is not intuitive, nesting depth increases, and true directive behavior cannot be achieved.
Solution 2: Babel Plugin
Implementation Idea
React JSX is essentially JavaScript syntax sugar, so a custom Babel plugin can transform attributes like r‑if into regular React code during the compilation phase.
Core principle: Using Babel AST transformation, replace <div r-if={count > 4}>I am larger than 4</div> with {count > 4 && <div>I am larger than 4</div>} .
With Vite, the plugin is added via @vitejs/plugin-react and the plugin file is specified in the Vite config.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
react({
babel: {
plugins: ["./babel-plugin-react-directives.js"],
},
}),
],
});The plugin itself exports a function that receives the Babel object, extracts types as t , and returns an object with a visitor handling JSXAttribute nodes whose name starts with r- . For r‑if , it finds the parent JSX element, replaces it with a logical‑AND expression container, and removes the original attribute.
export default function (babel) {
const { types: t } = babel;
return {
name: "react-directives",
visitor: {
JSXAttribute(path) {
if (path.node.name.name?.startsWith("r-")) {
const directive = path.node.name.name;
const condition = path.node.value?.expression;
if (directive === "r-if" && condition) {
const jsxElement = path.findParent(p => p.isJSXElement());
jsxElement.replaceWith(
t.jSXExpressionContainer(
t.logicalExpression("&&", condition, jsxElement.node)
)
);
path.remove();
}
}
},
},
};
}Result
After configuring Vite and adding the plugin, the custom directives work as expected, allowing concise JSX such as <div r-if={isShow}>... and <div r-for={item in items} ...>... .
Solution 3: Overriding createElement
An alternative idea is to monkey‑patch React.createElement at runtime to process r‑if , but this approach was not successful.
import React from "react";
const originalCreateElement = React.createElement;
const customCreateElement = function (type, props, ...children) {
// handle r-if directive
if (props && props["r-if"] === false) {
return null;
}
if (props && typeof props["r-if"] !== "undefined") {
return props["r-if"]
? originalCreateElement(
type,
{ ...props, "r-if": undefined },
...children
)
: null;
}
return originalCreateElement(type, props, ...children);
};
export const applyDirectives = () => {
if (!React.__directivesApplied) {
React.__directivesApplied = true;
React.createElement = customCreateElement; // apply override
}
};Then import and invoke applyDirectives() in main.jsx .
Conclusion
By creating a Babel plugin we successfully introduced Vue‑like directives into React, simplifying component code and demonstrating the power of AST manipulation. This is just a starting point, but it opens new possibilities for React developers.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.