Async Python orchestrator for HTML-first web applications
Project description
requirements.txt
fastapi>=0.68.0
uvicorn[standard]>=0.15.0
aiohttp>=3.8.0
loguru>=0.6.0
psutil>=5.8.0
python-multipart>=0.0.5
async-property>=0.2.1
HTMLnoJS
A Go-powered HTMX framework for building modern web applications without complex JavaScript.
Features
- ๐ Zero JavaScript required
- ๐ง Go-powered backend with Python handlers
- โก HTMX for seamless interactivity
- ๐ File-based routing system
- ๐จ Automatic CSS injection
- ๐ Hot reloading during development
- ๐ Python functions as API endpoints
Quick Start
Installation
pip install htmlnojs
Basic Usage
from htmlnojs import htmlnojs
# Create and run your app
app = htmlnojs("./my-project")
app.run_forever()
Project Structure
my-project/
โโโ templates/ # HTML files
โ โโโ index.html # โ serves at /
โ โโโ about.html # โ serves at /about
โโโ css/ # CSS files
โ โโโ main.css # Global styles
โ โโโ components.css # Component styles
โโโ py_htmx/ # Python HTMX handlers
โโโ demo.py # API handlers
โโโ utils.py # Utility functions
How It Works
HTMLnoJS combines the speed of Go with the simplicity of Python:
- Go Server: Serves HTML, CSS, and handles routing at blazing speed
- Python Handlers: Process HTMX requests via FastAPI
- Automatic Routing: Files become routes automatically
- Zero Config: Just create files and they work
HTML Templates
Create HTML files in the templates/ directory:
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>My HTMLnoJS App</title>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
<div class="container">
<h1>Welcome to HTMLnoJS</h1>
<!-- Interactive button -->
<button hx-get="/api/demo/hello" hx-target="#result" class="btn">
Click me for HTMX magic!
</button>
<div id="result"></div>
<!-- Form submission -->
<form hx-post="/api/demo/form" hx-target="#form-result">
<input type="text" name="message" placeholder="Enter a message" required>
<button type="submit">Submit</button>
</form>
<div id="form-result"></div>
</div>
</body>
</html>
Python Handlers
Create Python functions in the py_htmx/ directory:
# py_htmx/demo.py
def htmx_hello(request):
"""Simple HTMX handler"""
return '<div class="alert">Hello from HTMLnoJS! ๐</div>'
def htmx_form(request):
"""Handle form submission"""
message = request.get('message', 'No message provided')
return f'''
<div class="alert success">
<strong>Form submitted!</strong><br>
Your message: "{message}"
</div>
'''
def htmx_get_users(request):
"""GET request - fetch users"""
users = ['Alice', 'Bob', 'Charlie']
html = '<ul>'
for user in users:
html += f'<li>{user}</li>'
html += '</ul>'
return html
def htmx_post_user(request):
"""POST request - create user"""
name = request.get('name', '')
if name:
return f'<div class="success">Created user: {name}</div>'
return '<div class="error">Name is required</div>'
CSS Styling
Add CSS files in the css/ directory for automatic injection:
/* css/main.css */
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.btn {
background: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
.btn:hover {
background: #2980b9;
}
.alert {
padding: 15px;
margin: 10px 0;
border-radius: 4px;
background: #f8f9fa;
border-left: 4px solid #3498db;
}
.alert.success {
background: #d4edda;
border-color: #28a745;
color: #155724;
}
.alert.error {
background: #f8d7da;
border-color: #dc3545;
color: #721c24;
}
HTTP Method Control
Control HTTP methods via function naming:
# Explicit method specification
def htmx_get_hello(request): # โ GET /api/demo/hello
return '<div>Hello World!</div>'
def htmx_post_create(request): # โ POST /api/demo/create
return '<div>Created!</div>'
def htmx_put_update(request): # โ PUT /api/demo/update
return '<div>Updated!</div>'
def htmx_delete_remove(request): # โ DELETE /api/demo/remove
return '<div>Deleted!</div>'
# Automatic detection
def htmx_hello(request): # โ GET (default)
pass
def htmx_form(request): # โ POST (semantic detection)
pass
def htmx_create_user(request): # โ POST (contains "create")
pass
Advanced Usage
Async Context Manager
async with htmlnojs("./my-project") as app:
# Do other async work while server runs
await some_other_async_task()
# Server automatically stops when exiting
Manual Control
app = htmlnojs("./my-project", port=8080, verbose=True)
# Start the server
await app.start()
# Do other work...
await asyncio.sleep(10)
# Stop the server
await app.stop()
Multiple Instances
# Run multiple apps on different ports
app1 = htmlnojs("./frontend", port=3000)
app2 = htmlnojs("./admin", port=3001)
await app1.start()
await app2.start()
# Both servers running simultaneously
Signal Handling
# Graceful shutdown on Ctrl+C
app = htmlnojs("./my-project")
try:
app.run_forever() # Handles signals automatically
except KeyboardInterrupt:
print("Shutting down...")
Request Handling
The request parameter contains form data, query parameters, and more:
def htmx_example(request):
# Form data (POST requests)
username = request.get('username', '')
email = request.get('email', '')
# Query parameters (GET requests)
page = request.get('page', '1')
# Multiple values
tags = request.get('tags', []) # For multiple checkboxes
# Check if field exists
if 'username' in request:
return f'<div>Hello {username}!</div>'
else:
return '<div>Username required</div>'
File Organization
Large Applications
my-app/
โโโ templates/
โ โโโ index.html
โ โโโ auth/
โ โ โโโ login.html # โ /auth/login
โ โ โโโ register.html # โ /auth/register
โ โโโ dashboard/
โ โโโ index.html # โ /dashboard/
โโโ css/
โ โโโ main.css # Global styles
โ โโโ components.css # Reusable components
โ โโโ pages.css # Page-specific styles
โโโ py_htmx/
โโโ auth.py # Authentication handlers
โโโ dashboard.py # Dashboard handlers
โโโ utils.py # Utility functions
Handler Organization
# py_htmx/auth.py
def htmx_login(request):
username = request.get('username')
password = request.get('password')
# Handle login logic
return '<div>Login successful!</div>'
def htmx_register(request):
# Handle registration
return '<div>Registration complete!</div>'
# py_htmx/dashboard.py
def htmx_get_stats(request):
# Return dashboard statistics
return '<div class="stats">...</div>'
def htmx_update_profile(request):
# Handle profile updates
return '<div>Profile updated!</div>'
Error Handling
def htmx_safe_handler(request):
try:
# Your logic here
result = some_operation(request.get('data'))
return f'<div class="success">{result}</div>'
except ValueError as e:
return f'<div class="error">Invalid input: {e}</div>'
except Exception as e:
return f'<div class="error">Something went wrong: {e}</div>'
Best Practices
1. Return HTML Fragments
# Good - returns HTML fragment
def htmx_get_user(request):
return '<div class="user">John Doe</div>'
# Avoid - returns JSON (use regular API for this)
def htmx_get_user(request):
return {"name": "John Doe"} # Won't render in HTML
2. Use Semantic Class Names
<div class="user-card">
<button hx-delete="/api/users/delete"
hx-target="#user-list"
class="btn btn-danger">
Delete User
</button>
</div>
3. Handle Empty States
def htmx_get_items(request):
items = get_items_from_db()
if not items:
return '<div class="empty-state">No items found</div>'
html = '<div class="item-list">'
for item in items:
html += f'<div class="item">{item.name}</div>'
html += '</div>'
return html
4. Progressive Enhancement
<!-- Works without JavaScript, enhanced with HTMX -->
<form action="/api/contact/send" method="post"
hx-post="/api/contact/send" hx-target="#result">
<input type="email" name="email" required>
<button type="submit">Subscribe</button>
</form>
Configuration
Port Configuration
# Custom ports
app = htmlnojs(
project_dir="./my-app",
port=8080, # Go server port
verbose=True # Enable debug logging
)
Instance Management
from htmlnojs import list_instances, get, stop_all
# List all running instances
instances = list_instances()
# Get specific instance by ID
app = get("my-app-id")
# Stop all instances
await stop_all()
Troubleshooting
Common Issues
-
Port Already in Use
# HTMLnoJS automatically finds available ports app = htmlnojs("./my-project") # Will use 3000, 3001, etc.
-
Go Server Not Found
# HTMLnoJS includes Go server automatically # No need to install Go separately
-
Form Data Not Received
# Make sure to use the correct method def htmx_form(request): # POST by default message = request.get('message', '') # Will work
-
CSS Not Loading
# CSS files must be in css/ directory css/ โโโ main.css โ Will load โโโ styles/ โโโ theme.css โ Will load as styles/theme.css
Debug Mode
# Enable verbose logging
app = htmlnojs("./my-project", verbose=True)
app.run_forever()
# Check logs for detailed request/response info
Requirements
- Python 3.8+
- Go 1.19+ (automatically managed)
- Modern web browser with HTMX support
Performance
HTMLnoJS is designed for speed:
- Go server: Handles static files and routing at C-like speeds
- Python handlers: Only process dynamic content
- Minimal overhead: Direct proxy between Go and Python
- Concurrent: Handles multiple requests simultaneously
License
MIT License - see LICENSE file for details.
Project details
Release history Release notifications | RSS feed
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 htmlnojs-0.1.0.tar.gz.
File metadata
- Download URL: htmlnojs-0.1.0.tar.gz
- Upload date:
- Size: 51.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
778ade230b9db803787bc7a6744016d1e619da6db5fb1731527ef847d8866382
|
|
| MD5 |
3db405df92a29fe0c3524c876a758497
|
|
| BLAKE2b-256 |
ad4e46e46d6968c13c9ab48e7fc7d02203585528629c655c4da9e3ca0af4c513
|
File details
Details for the file htmlnojs-0.1.0-py3-none-any.whl.
File metadata
- Download URL: htmlnojs-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3b43fdccbc91d40d52cdccf53a97056fcdccf1e5c6b478cff959e90f3836bf9
|
|
| MD5 |
0e0928a5bd24b7aa754f8467f41361ba
|
|
| BLAKE2b-256 |
17f1e27807f63b5dcd47a86bcd2f389cebe97cf5171bde63529873b97086d196
|