Components
In JAF, a component is an HTML file.
There is no special file extension, no build configuration, and nothing to import.
Basic Structure
A component is any .html file with a PascalCase filename.
The filename becomes the tag name, so Button.html gives you
<Button />.
<!-- pages/components/Button.html -->
<script>
let count = 0
</script>
<button onclick="count++">Clicked {count} times</button>
<style>
button {
padding: 0.55rem 1.1rem;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--bg-tertiary);
color: var(--text-primary);
font: inherit;
font-weight: 500;
cursor: pointer;
transition: border-color 0.15s ease, background 0.15s ease;
}
button:hover {
border-color: var(--accent);
background: var(--bg-secondary);
}
</style>Use it anywhere, without importing it:
<!-- pages/index.html -->
<h1>Welcome</h1>
<Button />
The Three Parts
Components have three optional sections, in any order:
- <script> - JavaScript logic, reactive variables, functions
- Template - HTML markup with JEX expressions
- <style> - CSS that's scoped to this component by default
All three are optional. A component can be nothing but markup:
<!-- pages/components/Logo.html -->
<svg viewBox="0 0 100 100" width="40" height="40" aria-label="Logo">
<circle cx="50" cy="50" r="40" fill="currentColor" />
</svg>
Auto-Discovery
JAF scans your project for components.
Any .html file whose name
starts with an uppercase letter is a component; lowercase files under
pages/ are routes. Both kinds of file can sit side by side.
Select a file to see what it is for
Every component in that tree is available in every page and every other component, under its filename.
components/ is a convention for keeping them together, not a
requirement - StatsCard.html next to the dashboard page works
exactly the same.
Duplicate Names
Where a file sits only matters when two components share a name. The shallower
path wins - pages/Button.html beats
pages/admin/Button.html - and a file in the root
components/ folder is the last resort.
Two files claiming one name at the same depth is a build error naming both: nothing can decide between them. Rename one, or steer one into a namespace.
No Component Imports
Most frameworks make you register a component before you can use it. JAF has
no equivalent: there is no
import Button from "./components/Button.html", and writing one
leaves Button undefined.
Discovery by filename is the whole mechanism.
<script>
// JAF has no component import. This line does nothing:
// import Button from "./components/Button.html"
</script>
<!-- Button.html is found by filename - just use the tag. -->
<Button />
Naming a file on the tag is a build error
The alpha let a tag name its own file, so any tag name could be pointed at any component:
<!-- Every one of these is a build error now -->
<MyButton file="/pages/lib/ui/Button.html" />
<LocalWidget file="./widgets/Special.html" />
<SubmitBtn file="/pages/components/Button.html" />
A component is resolved to a module when the build runs, and a path only resolves once the app does. So the tag has to name a component the build compiled.
Move the file to where the name resolves, or give it the name you want to write. When two components genuinely need the same name, tell them apart with namespaces.
Imports in Scripts
Standard ES imports work in a component's <script> for npm
packages:
<script>
import { codeToHtml } from 'shiki'
import _ from 'lodash'
import debounce from 'lodash/debounce'
let code = ''
</script>
At compile time, JAF resolves package specifiers to Vite's pre-bundled deps.
The imported values are available throughout your component.