Documentation

Everything you need to start using RSFC in your React project.

Your First Component

A counter with state, a CSS Module, and auto-scoped styles — all in one file. The styles object is injected automatically when a <style module> block is present.

Counter.rsfc
1<script setup lang="ts">
2import { useState } from 'react'
3
4const [count, setCount] = useState(0)
5const double = count * 2
6</script>
7
8<template>
9<div className={styles.card}>
10 <p>{count} × 2 = {double}</p>
11 <button className={styles.btn} onClick={() => setCount(count + 1)}>
12 Increment
13 </button>
14</div>
15</template>
16
17<style module>
18.card { padding: 2rem; border-radius: 8px; background: #1b211d; }
19.btn { background: #42b883; color: #003823; border: none; cursor: pointer; }
20</style>

Quick Start

Vite

bash
1npm install -D @g-casau/rsfc-vite-plugin @vitejs/plugin-react
vite.config.ts
1import { defineConfig } from 'vite'
2import react from '@vitejs/plugin-react'
3import rsfc from '@g-casau/rsfc-vite-plugin'
4
5export default defineConfig({ plugins: [rsfc(), react()] })

Next.js

bash
1npm install -D @g-casau/rsfc-webpack-loader
next.config.ts
1const nextConfig = {
2 webpack(config) {
3 config.module.rules.push({
4 test: /\.rsfc$/,
5 use: [
6 { loader: 'babel-loader', options: { presets: [['@babel/preset-react', { runtime: 'automatic' }], '@babel/preset-typescript'] } },
7 { loader: '@g-casau/rsfc-webpack-loader' },
8 ],
9 })
10 return config
11 },
12}
13export default nextConfig

TypeScript

Add a declaration file so TypeScript recognises .rsfc imports:

rsfc.d.ts
1declare module '*.rsfc' {
2 import type { FC } from 'react'
3 const Component: FC
4 export default Component
5}

Blocks

BlockDescription
<script setup lang="ts">Shorthand — imports hoisted, code runs in component scope, default export generated automatically
<script lang="ts">Full control — write and export the component function yourself
<template>JSX returned by the component. styles is in scope automatically when a <style module> block is present
<style module>CSS Modules — class names are hashed, styles.* injected into scope
<style scoped>Scoped via a unique data-v-* attribute — no class renaming
<style lang="scss">Preprocessor support (Sass, Less, Stylus)
<clientScript>Browser-only code, skipped during SSR
<docs>Embedded Markdown documentation, readable via parseFile()
<anything>Custom blocks — handle via customBlockTransforms in the plugin

script vs script setup

<script setup> is the shorthand: variables are in scope and the default export is generated for you. <script> gives full control — write and export the function yourself.

script setup (shorthand)
1<script setup lang="ts">
2import { useState } from 'react'
3const [count, setCount] = useState(0)
4</script>
5
6<template>
7 <button onClick={() => setCount(count + 1)}>{count}</button>
8</template>
script (explicit)
1<script lang="ts">
2import { useState } from 'react'
3
4export default function Counter() {
5 const [count, setCount] = useState(0)
6 return <button onClick={() => setCount(count + 1)}>{count}</button>
7}
8</script>

Plugin Options

OptionTypeDefaultDescription
includestring[]['**/*.rsfc']Glob patterns of files to transform
excludestring[]['node_modules/**']Glob patterns of files to skip
customBlockTransformsRecord<string, Function>Transform custom blocks to JavaScript by tag name
vite.config.ts
1rsfc({
2 include: ['**/*.rsfc'], // default
3 exclude: ['node_modules/**'], // default
4 customBlockTransforms: {
5 graphql: (block) => `export const QUERY = \`${block.content}\``,
6 i18n: (block, id) => `export const messages = ${block.content}`,
7 },
8})

CLI

Compile or inspect any .rsfc file without a bundler:

bash
1# compile to standalone JavaScript
2rsfc compile src/App.rsfc
3rsfc compile src/App.rsfc -o dist/App.js
4
5# parse to JSON descriptor
6rsfc parse src/App.rsfc
7rsfc parse src/App.rsfc | jq '.docs'
CommandDescription
rsfc compile <file>Compile .rsfc to JavaScript (stdout)
rsfc compile <file> -o <out>Compile and write to file
rsfc parse <file>Parse and emit JSON descriptor (stdout)

Packages

PackageDescription
@g-casau/rsfc-vite-pluginVite plugin
@g-casau/rsfc-webpack-loaderWebpack / Next.js loader
@g-casau/rsfc-coreParser + generator — zero runtime deps
@g-casau/rsfc-cliCLI (rsfc compile, rsfc parse)

VS Code Extension

Install the RSFC extension for syntax highlighting, IntelliSense, and CSS Module autocompletion.

Download .vsix from GitHub Releases

Install via Extensions → Install from VSIX… in VS Code.

AI Setup

Add RSFC context to your AI editor so it understands the format and generates correct files. Copy this snippet into your CLAUDE.md, .github/copilot-instructions.md, or .cursorrules:

CLAUDE.md / copilot-instructions.md
1# RSFC — React Single File Components
2
3## Format
4
5Files use the `.rsfc` extension with Vue-like blocks:
6
7- `<script setup lang="ts">` — shorthand, imports hoisted, code in component scope
8- `<script lang="ts">` — explicit, write the full function yourself
9- `<template>` — JSX; `styles` auto-injected with `<style module>`
10- `<style module>` — CSS Modules, `styles.*` in scope
11- `<style scoped>` — scoped via `data-v-*` attribute
12- `<clientScript>` — browser-only, skipped during SSR
13- `<docs>` — embedded Markdown, readable via `parseFile()`
14
15## Example
16
17```rsfc
18<script setup lang="ts">
19import { useState } from 'react'
20const [count, setCount] = useState(0)
21</script>
22
23<template>
24 <button onClick={() => setCount(count + 1)}>{count}</button>
25</template>
26
27<style module>
28button { background: #42b883; color: #003823; }
29</style>
30```