#🍳 Flaxon Fyr.js
Author of Fyr.js: ALdane Hutchinson Author of Flaxon : Aldane Hutchinson
Table of Contents
Fyr.js reactive frontend integration plugin for Flaxon framework.
What is Fyr.js?
Fyr.js is a CDN-only, HTML-first reactive JavaScript framework. It lets you build interactive web applications without installing Node.js, npm, or any build tools. Just add a script tag and use HTML directives.
Website: https://fyrjsorg.vercel.app/
Features
- 🚀 CDN-only — No installation required, just script tags
- 📦 Reactive state — Automatic DOM updates when state changes
- 🎯 Server actions — Call backend endpoints with
Fyr.action - 🔄 State hydration — Pass server data to Fyr frontend
- 🛡️ CSRF protection — Built-in CSRF for actions
- 🔌 Plugin integration — Seamless Flaxon plugin loading
- 📝 Template rendering — Render Fyr HTML with Jinja2
- 🎨 Flexible assets — CDN or local asset serving
Installation
pip install flaxon-fyr
Quick Start
1. Load the Plugin
from flaxon import Flaxon
from flaxon_fyr import FyrPlugin
app = Flaxon("my-app")
await app.plugins.load_plugin(FyrPlugin(
cdn_version="0.1.2",
))
2. Create a Route with a Fyr App
FyrPlugin doesn't export a standalone fyr_app() function — call render_app() on the plugin instance instead. From inside a route handler, the current app is available as request.app, so the plugin is reachable at request.app.state.fyr:
@app.get("/")
async def home(request):
return request.app.state.fyr.render_app(
"counter",
state={"count": 0},
template="templates/counter.html"
)
3. Define a Fyr Controller (JavaScript)
// app.js
Fyr.createApp("counter", {
state: { count: 0 },
methods: {
increment() {
this.state.count += 1;
},
decrement() {
this.state.count -= 1;
}
}
});
Fyr.start("counter");
4. HTML Template
<!-- templates/counter.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Counter</title>
<script defer src="{{ fyr_cdn_url }}"></script>
<script defer src="/app.js"></script>
</head>
<body>
<main fyr-app="counter">
<h1>Counter: <span fyr-text="count"></span></h1>
<button fyr-click="increment()">+</button>
<button fyr-click="decrement()">-</button>
</main>
</body>
</html>
Server Actions
Define backend actions that Fyr can call. @fyr_action registers the handler into a global registry that FyrPlugin picks up automatically when it's constructed — so make sure your action modules are imported before you build the plugin instance:
from flaxon_fyr import fyr_action
@fyr_action("todos.create")
async def create_todo(data, request):
todo = await create_todo_in_db(data["text"])
return {"success": True, "data": todo}
@fyr_action("todos.list")
async def list_todos(data, request):
todos = await get_all_todos()
return {"data": todos}
Every action handler must accept (data, request), even if it doesn't use data.
Call from frontend:
const result = await Fyr.action.call("todos.create", { text: "Learn Fyr" });
if (result.success) {
console.log(result.data);
}
You can also register actions on an existing plugin instance directly:
async def create_todo(data, request):
todo = await create_todo_in_db(data["text"])
return {"success": True, "data": todo}
plugin = FyrPlugin()
plugin.register_action("todos.create", create_todo)
await app.plugins.load_plugin(plugin)
Configuration
Environment Variables
# Fyr CDN version
FYR_CDN_VERSION=0.1.2
# Action prefix
FYR_ACTION_PREFIX=/_fyr/actions
# CSRF enabled
FYR_CSRF_ENABLED=true
# Use local assets
FYR_USE_LOCAL_ASSETS=false
# Debug mode
FYR_DEBUG=false
With Flaxon Config
app = Flaxon("my-app", config={
"FYR_CDN_VERSION": "0.1.2",
"FYR_ACTION_PREFIX": "/api/actions",
"FYR_CSRF_ENABLED": True,
})
plugin = FyrPlugin.from_config(app.config)
await app.plugins.load_plugin(plugin)
Advanced Usage
State Hydration
Pass server data to Fyr frontend:
from flaxon_fyr import hydrate
@app.get("/dashboard")
async def dashboard(request):
user = await get_current_user(request)
tasks = await get_user_tasks(user.id)
state = hydrate({
"user": {"id": user.id, "name": user.name},
"tasks": [{"id": t.id, "text": t.text, "done": t.done} for t in tasks],
"flash": request.session.pop("flash", {}),
})
return request.app.state.fyr.render_app("dashboard", state)
Custom Templates
@app.get("/custom")
async def custom(request):
return request.app.state.fyr.render_app(
"custom-app",
state={"message": "Hello from server!"},
template="templates/custom.html",
context={
"page_title": "Custom Page",
"user": request.session.get("user"),
}
)
Multiple Fyr Apps
@app.get("/multi")
async def multi_apps(request):
return request.app.state.fyr.render_app(
"multi",
state={
"counter": {"count": 0},
"todo": {"items": [], "draft": ""},
}
)
<main fyr-app="multi">
<section>
<h2>Counter</h2>
<span fyr-text="counter.count"></span>
<button fyr-click="counter.count++">+</button>
</section>
<section>
<h2>Todo</h2>
<input fyr-model="todo.draft">
<button fyr-click="todo.items.push({id: Date.now(), text: todo.draft})">
Add
</button>
<template fyr-for="item in todo.items" fyr-key="item.id">
<p fyr-text="item.text"></p>
</template>
</section>
</main>
CSRF Protection
CSRF is on by default. The cookie is issued automatically on the first response and validated (via the X-CSRFToken header against the cookie) on every action request:
# Enabled by default
await app.plugins.load_plugin(FyrPlugin(
csrf_enabled=True,
))
# Disable for development
await app.plugins.load_plugin(FyrPlugin(
csrf_enabled=False,
))
FyrPlugin.__init__ doesn't take csrf_cookie_name/csrf_header_name directly — to change those, build a FyrPluginConfig and pass it as config=:
from flaxon_fyr import FyrPlugin
from flaxon_fyr.plugin import FyrPluginConfig
await app.plugins.load_plugin(FyrPlugin(
config=FyrPluginConfig(
csrf_enabled=True,
csrf_cookie_name="csrf_token",
csrf_header_name="X-CSRFToken",
),
))
The current CSRF token for the request is available at request._csrf_token inside a route handler, if you need to render it into a form manually.
Local Asset Serving
# Serve Fyr assets from local static directory
await app.plugins.load_plugin(FyrPlugin(
use_local_assets=True,
assets_path="static/fyr",
))
# The plugin will serve:
# /fyr/assets/fyr.js
# /fyr/assets/fyr.min.js
# /fyr/assets/fyr-python.js
# etc.
Fyr CDN Assets
| Asset | URL |
|---|---|
| Core | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr.min.js |
| ESM | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr.esm.js |
| Router | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-router.min.js |
| Python | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-python.min.js |
| WASM | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-wasm.min.js |
| Socket | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-socket.min.js |
| UI | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-ui.min.js |
| UI CSS | https://cdn.jsdelivr.net/npm/@aldane-dev-create/fyr@0.1.2/dist/fyr-ui.css |
Testing
# Run all tests
pytest
# Run with coverage
pytest --cov=flaxon_fyr
# Run specific test
pytest tests/test_actions.py -v
Project Structure
flaxon-fyr/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│ └── flaxon_fyr/
│ ├── __init__.py # Public API exports
│ ├── plugin.py # FyrPlugin class
│ ├── renderer.py # Fyr template rendering
│ ├── actions.py # Server action handler
│ ├── assets.py # Asset serving (CDN/local)
│ ├── hydration.py # Server-to-client state hydration
│ ├── middleware.py # Fyr middleware (CSRF, etc.)
│ └── types.py # Type definitions
└── tests/
├── test_plugin.py
├── test_renderer.py
├── test_actions.py
└── test_integration.py
Security Best Practices
✅ Use CSRF protection in production
✅ Validate all action inputs on server
✅ Authenticate actions with session
✅ Use HTTPS in production
✅ Keep Fyr version pinned
✅ Use Subresource Integrity for CDN assets
✅ Sanitize data passed to fyr-html
✅ Never put secrets in frontend code
Fyr Directives Reference
| Directive | Purpose | Example |
|---|---|---|
fyr-app |
Mark application root | fyr-app="todo" |
fyr-controller |
Attach state and methods | fyr-controller="cart" |
fyr-text |
Render escaped text | fyr-text="user.name" |
fyr-html |
Render trusted HTML | fyr-html="trustedContent" |
fyr-model |
Two-way form binding | fyr-model="form.email" |
fyr-click |
Handle click | fyr-click="save()" |
fyr-on:event |
Handle any event | fyr-on:input="search()" |
fyr-show |
Toggle visibility | fyr-show="loggedIn" |
fyr-if |
Create/remove content | fyr-if="items.length" |
fyr-for |
Repeat template | fyr-for="item in items" |
fyr-submit |
Handle form submit | fyr-submit="login()" |
fyr-bind:* |
Bind attribute/property | fyr-bind:disabled="busy" |
fyr-class |
Bind CSS classes | fyr-class="{ active: selected }" |
fyr-style |
Bind styles | fyr-style="{ width: progress + '%' }" |
fyr-ref |
Store DOM reference | fyr-ref="emailInput" |
fyr-init |
Run startup method | fyr-init="load()" |
fyr-cloak |
Hide before initialization | fyr-cloak |
fyr-transition |
Apply transition hooks | fyr-transition="fade" |
Roadmap
| Version | Features |
|---|---|
| 0.1.0 | Basic Fyr integration, CDN serving |
| 0.2.0 | Server actions with CSRF |
| 0.3.0 | State hydration and session integration |
| 0.4.0 | Component rendering |
| 0.5.0 | Asset serving (local) |
| 0.6.0 | Fyr router integration |
Related Plugins
- flaxon-jinax - Jinja2 template integration
- flaxon-sentry - Sentry error tracking
- flaxon-oauth-google - Google OAuth
- flaxon-inertia - Inertia.js integration
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new features
- Ensure all tests pass
- Submit a pull request
License
MIT License - See LICENSE file for details.
Support
- 📚 Documentation
- 🐛 Issue Tracker
- 💬 Discussions
- 🌐 Fyr.js Website
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flaxon_fyr-0.1.1.tar.gz.
File metadata
- Download URL: flaxon_fyr-0.1.1.tar.gz
- Upload date:
- Size: 27.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ac5dccb075b24fb73e89e23723ea7c3d865c2df7405bb5f322d0bed7759b26e
|
|
| MD5 |
b08c2cf70eabb2049696141c4a2b8798
|
|
| BLAKE2b-256 |
333c51fd9d953739c73c698c8035446bceec4a1aff760f25faf45cef4bf36110
|
File details
Details for the file flaxon_fyr-0.1.1-py3-none-any.whl.
File metadata
- Download URL: flaxon_fyr-0.1.1-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e52d9381d91422d98e6b2e1dcf91da2bc59b40de15572a65f86b52df69211eb
|
|
| MD5 |
d80e00922f3bc67022357ae595f25b76
|
|
| BLAKE2b-256 |
9df5ac275097ab37c11bc11b7dfc7fbeaad71a2981d8a4f0b4c5b650f8660fe7
|