react-sfc — Why I Built a Vue-Style Single File Component System for React
I've been a Vue developer for years. Not the kind who picked it by accident or because it was on the job description — the kind who genuinely thinks the Vue mental model is one of the best things to happen to frontend development. Single File Components in particular. The idea that your template, your logic, and your styles live together in one clean, clearly separated file just makes sense to me.
But I'm not naive enough to think Vue is the only world worth knowing. React dominates job boards, open source, and conversations at every tech conference. So at some point, I made the decision to stop dancing around it and actually go deep.
That's where the trouble started.
The problem with React's templating — for a Vue developer
JSX is not bad. I want to be clear about that upfront. It's a deliberate design choice with real advantages, and millions of developers work with it happily every day.
But for me, coming from Vue SFCs, it felt wrong in a way I couldn't shake.
The HTML mixed directly into JavaScript return statements. Ternaries for conditional rendering. The constant context-switching between "I'm writing markup" and "I'm writing logic" happening in the same breath. In Vue, the <template> block is just HTML — clean, readable, obvious. In React, the line between markup and code is blurry by design.
I know this is partly a matter of habit. But I also think it's a real friction point for developers who come from a template-first background. And instead of just grinding through it, I asked myself a different question:
What if I didn't have to?
The experiment — building a parser instead of adapting
The idea was simple: write React components the way I'd write Vue components, and let a parser handle the transformation.
A .rsfc file would look like this:
<template>
<div class="counter">
<p>Count: {count}</p>
<button onClick={increment}>+</button>
</div>
</template>
<script>
import { useState } from 'react';
export function useLogic() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
</script>
<style>
.counter { display: flex; flex-direction: column; gap: 8px; }
</style>
The parser would take that, extract each block, wire the logic to the template, and output valid React code. No JSX in your face. No mixed concerns. Just the separation I was used to.
I built the first working version over two or three weekends, with Claude helping me think through the parsing logic and edge cases. It was a genuine collaboration — I knew what I wanted the output to look like, Claude helped me reason through how to get there. The result was rough but functional: a Vite plugin that processed .rsfc files and compiled them into React components.
The VS Code plugin — a rabbit hole I didn't expect
Once I had something working, I shared it. The reaction was small but real — a few people found it interesting, asked questions, opened issues. That was enough to push me toward the next step: proper editor support.
I'd never built a VS Code extension before. I assumed it would be a weekend project. It was not.
The complexity of language tooling surprised me. Syntax highlighting is straightforward enough, but getting real support — IntelliSense, type checking, hover docs inside a custom file format — requires hooking into the language server ecosystem. That's where Volar came in. Volar is the framework that powers Vue's VS Code support, and it's designed exactly for this kind of embedded language scenario. Using it as a foundation made the plugin actually viable.
It's still one of the more technically interesting things I've built, even if the use case is deliberately niche.
What react-sfc can do today
The project is functional and usable. Here's what's currently supported:
.rsfcfile format with<template>,<script>, and<style>blocks- Vite plugin for compilation
- VS Code extension with syntax highlighting and basic language support
- Scoped styles
- TypeScript support in the script block Is it production-ready for a large codebase? Probably not yet. Is it a working proof of concept that makes React feel more approachable for Vue developers? Yes, genuinely.
Why this project exists (and the honest answer)
I'll be transparent about something: react-sfc started as a learning experiment with a slight troll energy. The Vue community has a long-running joke about JSX, and building a whole SFC system for React is a very specific way of saying "I really don't like this."
But underneath the joke, there's a real point. Developer experience matters. The way you write code affects how you think about it. And the fact that I learned more about React internals, Vite plugins, VS Code extension development, and language servers by building this than I would have by just writing JSX for six months — that feels like the real win.
Try it, break it, contribute
The project is open source and lives here:
- GitHub: github.com/GeoffreyCasaubon/react-sfc
- Demo & docs: geoffreycasaubon.github.io/react-sfc If you're a Vue developer curious about React, it might make the jump feel less jarring. If you're a React developer who thinks this is heresy, I genuinely want to hear why. And if you want to contribute — whether that's improving the parser, extending the VS Code plugin, or just opening an issue — you're very welcome.
Stars are also appreciated. I won't pretend otherwise.