One thing I've always loved about Vue is its Single-File Component (SFC) approach. In a single .vue file, you colocate JavaScript logic, template markup, and CSS styles. Everything that belongs to a component lives together, which makes components easy to reason about, refactor, and move around.
I wanted that same feeling when working with Preact.
Preact doesn't prescribe a single-file format like Vue does, but it's flexible enough that you can build your own conventions. This article describes my own approach to single-file Preact components, inspired by Vue SFCs, without introducing a build step, custom compilers, or framework-level abstractions.
The Goal
- Keep component logic, markup, and styles in one file
- Avoid complex tooling or build steps
- Stay close to idiomatic Preact
- Make components easy to read and move
The Core Idea
A single-file Preact component in this style contains a normal Preact function component, its markup, and its styles defined right next to each other.
The only custom utilities involved are small html and css template tags. There's no magic beyond that.
Minimal Example
// ExampleComponent.ts
import { html, css } from "@faunder/markup"
import { useState } from "preact/hooks"
export function MyComponent() {
const [count, setCount] = useState(0)
const view = html`
<div data-scope="MyComponent">
<h1>Hello World</h1>
<p>Count: ${count}</p>
<button onClick=${() => setCount((c) => c + 1)}>
Increment
</button>
</div>
`
const style = css`
@scope ([data-scope="MyComponent"]) to ([data-scope]) {
h1 {
color: var( --primary-600);
}
}
`
return [view, style]
}The css Helper
The css helper is intentionally boring. It's just a template tag that returns a <style> node containing raw CSS. I keep these small helpers in Faunder Markup, linked below.
There's no runtime, no hashing, and no transformation step. What you write is what ends up in the DOM.
Why I Like This Approach
- Colocation – logic, markup, and styles live together
- Simplicity – no custom build pipeline or compiler
- Predictability – plain Preact components
- Easy refactoring – move or delete a component as a single file
It feels very close to Vue's SFC philosophy, but implemented entirely at the user-land level.
Closing Thoughts
Preact doesn't force you into a specific component format — and that's a strength. With small helpers and modern CSS, you can recreate much of what makes Vue SFCs enjoyable, while staying fully within the Preact ecosystem.