# Preact No-Build Setup - Developer Guide
## Overview
This project uses **Preact with HTM** loaded via ES modules from `esm.sh` - **zero build setup required**. Templates remain Jinja2-based on the server, with Preact handling client-side interactivity.
## Architecture
```
templates/ # Original Jinja2 templates (unchanged)
templates_preact/ # New Preact-enabled templates
static/js/components/ # Reusable Preact components
template_utils.py # Separate template render functions
```
## Template Render Functions
Two separate render functions keep template directories isolated:
### `render_template()` - Regular Templates
```python
from flask import render_template
@main_bp.route('/dashboard')
def dashboard():
return render_template('dashboard.html', sessions=sessions)
```
### `render_esm_template()` - Preact/ESM Templates
```python
from template_utils import render_esm_template
@main_bp.route('/beta/dashboard')
def beta_dashboard():
return render_esm_template('dashboard.html',
current_app.config['TEMPLATES_PREACT'],
sessions=sessions)
```
## Quick Start
### 1. Extend the Preact Base Template
```html+jinja
{% extends "base_preact.html" %}
{% block title %}My Page{% endblock %}
{% block content %}
{% endblock %}
```
### 2. Pass Server Data to JavaScript
```html+jinja
{% block scripts %}
{{ super() }}
{% endblock %}
```
### 3. Create Your Preact App
```html+jinja
```
## Using Components
### Import Core Components
```javascript
import {
Button, Card, Badge, Alert,
FormInput, FormSelect, Modal,
Table, ProgressBar, Spinner
} from '/static/js/components/core.js';
import { FileUpload, FilePreviewGrid } from '/static/js/components/FileUpload.js';
import { DashboardTable, FilterTabs, BulkActionsBar } from '/static/js/components/DataTable.js';
```
### Example: Using Components
```javascript
function MyComponent() {
const html = window.html;
const [loading, setLoading] = useState(false);
return html`
<${Card} title="Upload Files">
<${FileUpload}
accept="image/*"
multiple=${true}
onFilesSelected=${(files) => console.log(files)}
/>
<${Button}
variant="success"
loading=${loading}
onClick=${() => {
setLoading(true);
// do something
}}
>
Upload
${Button}>
${Card}>
`;
}
```
## Component API
### Button
```javascript
<${Button}
variant="primary|secondary|success|danger|warning|info|outline"
size="sm|lg"
pill=${true}
disabled=${false}
loading=${false}
icon="bi bi-check"
onClick=${handler}
>
Click Me
${Button}>
```
### Card
```javascript
<${Card}
title="Card Title"
header=${html`Custom Header
`}
footer=${html`Custom Footer
`}
hoverable=${true}
>
Content here
${Card}>
```
### Modal
```javascript
const [showModal, setShowModal] = useState(false);
// ...
<${Modal}
show=${showModal}
title="Modal Title"
size="md|lg|xl|sm"
staticBackdrop=${true}
onClose=${() => setShowModal(false)}
footer=${html`
`}
>
Modal content
${Modal}>
```
### FormInput
```javascript
<${FormInput}
label="Email"
type="email"
value=${email}
onInput=${(e) => setEmail(e.target.value)}
error=${errors.email}
helpText="We'll never share your email"
placeholder="you@example.com"
/>
```
### DashboardTable
```javascript
<${DashboardTable}
data=${sessions}
columns=${columns}
actions=${actions}
onSelectionChange=${(ids) => setSelectedIds(ids)}
onRowUpdate=${(id, updates) => handleUpdate(id, updates)}
/>
```
### FileUpload
```javascript
<${FileUpload}
accept="image/*"
multiple=${true}
maxFiles=${10}
onFilesSelected=${(files) => setFiles(files)}
/>
<${FilePreviewGrid}
files=${files}
onFilesChange=${setFiles}
sortable=${true}
/>
```
## Available Components
### Core (`/static/js/components/core.js`)
- `Button` - Styled buttons with variants, sizes, loading state
- `Card` - Card container with optional header/footer
- `Badge` - Status badges with color variants
- `Alert` - Alert messages with dismiss option
- `FormInput` - Text input with label and validation
- `FormSelect` - Select dropdown
- `Modal` - Bootstrap modal wrapper
- `Spinner` - Loading spinner
- `Table` - Data table
- `ProgressBar` - Progress indicator
- `Tabs` - Tab navigation
- `Checkbox` - Checkbox input
- `Toggle` - Toggle switch
- `FileUpload` - File input trigger
- `SearchInput` - Debounced search input
### DataTable (`/static/js/components/DataTable.js`)
- `DashboardTable` - Full-featured table with selection & inline editing
- `FilterTabs` - Filter/pagination tabs
- `BulkActionsBar` - Bulk action toolbar
### FileUpload (`/static/js/components/FileUpload.js`)
- `FileUpload` - Drag & drop upload zone
- `FilePreviewCard` - Single file preview
- `FilePreviewGrid` - Grid of file previews with drag-reorder
- `UploadInterface` - Complete upload UI
## Patterns
### Hybrid Rendering (Jinja2 + Preact)
Server renders initial state, Preact takes over:
```html+jinja
{# Server passes data #}
{# Preact hydrates the app #}
```
### Server Actions with CSRF
```javascript
const handleSave = async (data) => {
const response = await fetch('/api/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': window.INITIAL_DATA.csrfToken
},
body: JSON.stringify(data)
});
return await response.json();
};
```
### Navigation
```javascript
// Full page navigation
window.location.href = `/edit/${id}`;
// Or use anchor tags in JSX
html`Edit`
```
## Migration Strategy
### Phase 1: New Features
Create new pages in `templates_preact/` using Preact
### Phase 2: Incremental Migration
Migrate complex interactive pages:
1. File upload interfaces
2. Data tables with filtering
3. Form-heavy pages
4. Real-time updates
### Phase 3: Component Extraction
Extract common patterns into reusable components
## File Structure
```
Report-Generator/
├── templates/ # Original Jinja2 templates
│ ├── base.html
│ ├── dashboard.html
│ └── ...
├── templates_preact/ # New Preact templates
│ ├── base_preact.html # Preact-enabled base
│ ├── demo.html # Demo page
│ └── dashboard.html # Preact dashboard
└── static/
└── js/
└── components/
├── core.js # Core UI components
├── DataTable.js # Data table components
└── FileUpload.js # Upload components
```
## Benefits
✅ **No build setup** - Direct ES modules from CDN
✅ **Tiny footprint** - Preact is ~3KB
✅ **Modern JSX-like syntax** - Via HTM
✅ **Full hooks support** - useState, useEffect, etc.
✅ **Backwards compatible** - Original templates unchanged
✅ **Progressive enhancement** - Migrate at your pace
## CDN Links
- Preact: `https://esm.sh/preact@10.19.3`
- Preact Hooks: `https://esm.sh/preact@10.19.3/hooks`
- HTM: `https://esm.sh/htm@3.1.1`
## Troubleshooting
### Components not rendering
- Ensure `window.html = htm.bind(h)` is called
- Check browser console for import errors
- Verify component exports match imports
### State not updating
- Remember Preact uses `useState`, not `this.state`
- Use `setState(newValue)`, not direct assignment
### Server data not available
- Pass data via `window.INITIAL_DATA` before module script
- Use `{{ data | tojson }}` for safe JSON serialization
## Examples
See these templates for reference:
- `templates_preact/demo.html` - Basic component usage
- `templates_preact/dashboard.html` - Full data table implementation