The secure-by-default, islands-first micro-framework.
NPM Version License: ISC CI Bundle Size
π Website | π Documentation | π» Examples
- Install
- Hello World
- Why front.js?
- Core Concepts
- Examples
- API Reference
- Limitations
- Security Model
- Architecture
- Development
- Contributing
- License
This repository is organized as a monorepo containing:
- @frontjs/core - The runtime (<5KB) with Islands Architecture hydration
- @frontjs/actions - Type-safe command/RPC layer with Standard Schema validation
# Install the core runtime npm install @frontjs/core uhtml # Optional: Install actions for type-safe server communication npm install @frontjs/actions
Or use directly via CDN:
<script type="importmap"> { "imports": { "front-js": "https://esm.sh/front-js@0.0.1", "uhtml": "https://esm.sh/uhtml@4.5.11" } } </script>
1. HTML - Mark interactive areas with data-island:
Output your HTML with data-island, data-component, and data-props.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>My App</title> </head> <body> <div data-island data-component="Counter" data-props='{"start": 10}'></div> <script type="importmap"> { "imports": { "uhtml": "https://esm.sh/uhtml@4.5.11" } } </script> <script type="module" src="./app.js"></script> </body> </html>
2. JavaScript - Register your component and hydrate:
import { html, val, register, hydrate } from './src/index.js'; function Counter(props) { const count = val(props.start || 0); return () => html` <div> <button onclick=${() => count(count() - 1)}>-</button> <span>Count: ${count()}</span> <button onclick=${() => count(count() + 1)}>+</button> </div> `; } register('Counter', Counter); hydrate();
The html tag returns a template object, not a string:
// β CORRECT: Use html tag with render() const template = html`<div>Hello</div>`; render(container, template); // β WRONG: html tag with string API element.innerHTML = html`<div>Hello</div>`; // Shows "[object Object]" // β CORRECT: Use plain template literal for strings element.innerHTML = `<div>Hello</div>`;
π See Template Tags vs Strings Guide for details.
- π Islands Architecture: Hydrate only what needs interaction.
- π Secure by Default: Data flows via JSON only. No server closures.
- β‘ Tiny Runtime: <5KB gzipped. No build step required.
- π‘ Sanitized Rendering: Powered by
uhtmlto prevent XSS. - π― Fine-Grained Reactivity: Value-based state management (val/run/calc) with automatic dependency tracking.
Note: Built in response to recent security concerns with React Server Components (context).
Values are reactive primitives that track dependencies automatically:
import { val, run } from './src/index.js'; const count = val(0); // Read value count(); // 0 // Update value count(5); // Updates to 5, notifies subscribers // Read without subscribing count.peek(); // 5 (doesn't track dependency) // Auto-track in runs run(() => { console.log('Count changed:', count()); });
Components are functions that return render functions:
function MyComponent(props) { const state = val(props.initialValue); return () => html` <div>Value: ${state()}</div> `; }
Components are hydrated from server-rendered HTML:
<div data-island data-component="MyComponent" data-props='{"initialValue": 42}'></div>
Runs can clean up side effects like timers, event listeners, and subscriptions:
function Timer(props) { const seconds = val(0); run(() => { const interval = setInterval(() => { seconds(seconds() + 1); }, 1000); // Cleanup when run re-executes or component disposes return () => clearInterval(interval); }); return () => html`<div>Time: ${seconds()}s</div>`; }
Components can be manually disposed via container._front_dispose() for cleanup when using frameworks like HTMX:
// HTMX integration example document.body.addEventListener('htmx:beforeSwap', (event) => { const island = event.detail.target.querySelector('[data-island]'); if (island && island._front_dispose) { island._front_dispose(); // Runs cleanup functions } });
See the examples/ directory for complete working examples, including a Todo app that demonstrates all framework features.
To run examples:
npx serve . # Navigate to http://localhost:3000/examples/index.html
See wiki/API.md for complete API documentation.
val(initialValue)- Create reactive valuerun(fn)- Run code reactivelycalc(fn)- Create calculated (derived) valueregister(name, componentFn)- Register componenthydrate(root?)- Hydrate islands in DOMhtml\template`` - Safe template literal (from uhtml)render(container, template)- Render template (from uhtml)
front.js is designed for server-rendered apps with Islands Architecture. See docs/LIMITATIONS.md for:
- Known constraints and trade-offs
- When NOT to use front.js
- Performance considerations
- Workarounds for common issues
front.js assumes the HTML is the Source of Truth.
- No eval: We never execute strings from the DOM.
- Explicit Props: Data must be serialized to JSON.
- Strict Content:
uhtmlescapes all values by default. - Component Validation: Component names are validated (alphanumeric only).
- Zero Trust: Invalid islands are logged and skipped, never crash the app.
front.js follows the "Islands Architecture" pattern:
- Server renders HTML with
data-islandmarkers - Client hydrates only interactive islands
- Data flows via JSON in
data-propsattributes - No magic - explicit component registration
See docs/BLUEPRINT.md for detailed architecture documentation.
# Install dependencies npm install # Build npm run build # Check bundle size npm run size-check # Run tests npm test # Format code npm run format
See CONTRIBUTING.md for development guidelines.
See DEVELOPMENT.md for how to run the website and KB locally, and deployment instructions.
ISC