# 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 `; } ``` ## 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 ``` ### Card ```javascript <${Card} title="Card Title" header=${html`
Custom Header
`} footer=${html`
Custom Footer
`} hoverable=${true} > Content here ``` ### 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 ``` ### 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