Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

A secure, <2KB micro-framework for hydrating server-rendered HTML islands. Zero build, fine-grained reactivity (val/run/calc).

License

Notifications You must be signed in to change notification settings

watthem/front-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

21 Commits

Repository files navigation

frontjs

The secure-by-default, islands-first micro-framework.

NPM Version License: ISC CI Bundle Size

🌐 Website | πŸ“š Documentation | πŸ’» Examples

Table of Contents

Monorepo Structure

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

# 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>

Hello World

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();

⚠️ Important: html Tag vs Plain Template Literals

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.

Why front.js?

License: ISC CI

  • 🏝 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 uhtml to 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).

Core Concepts

Values

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

Components are functions that return render functions:

function MyComponent(props) {
 const state = val(props.initialValue);
 return () => html` <div>Value: ${state()}</div> `;
}

Hydration

Components are hydrated from server-rendered HTML:

<div data-island data-component="MyComponent" data-props='{"initialValue": 42}'></div>

Lifecycle Cleanup

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
 }
});

Examples

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

API Reference

See wiki/API.md for complete API documentation.

Quick Reference

  • val(initialValue) - Create reactive value
  • run(fn) - Run code reactively
  • calc(fn) - Create calculated (derived) value
  • register(name, componentFn) - Register component
  • hydrate(root?) - Hydrate islands in DOM
  • html\template`` - Safe template literal (from uhtml)
  • render(container, template) - Render template (from uhtml)

Limitations

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

Security Model

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: uhtml escapes all values by default.
  • Component Validation: Component names are validated (alphanumeric only).
  • Zero Trust: Invalid islands are logged and skipped, never crash the app.

Architecture

front.js follows the "Islands Architecture" pattern:

  1. Server renders HTML with data-island markers
  2. Client hydrates only interactive islands
  3. Data flows via JSON in data-props attributes
  4. No magic - explicit component registration

See docs/BLUEPRINT.md for detailed architecture documentation.

Development

# Install dependencies
npm install
# Build
npm run build
# Check bundle size
npm run size-check
# Run tests
npm test
# Format code
npm run format

Contributing

See CONTRIBUTING.md for development guidelines.

See DEVELOPMENT.md for how to run the website and KB locally, and deployment instructions.

License

ISC

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /