# Code Style and Conventions Standards for HTMX
This document outlines the code style and conventions standards for HTMX development. Following these guidelines ensures consistency, readability, maintainability, and ultimately, higher-quality HTMX applications. This document is intended to guide developers and inform AI coding assistants.
## 1. General Formatting and Style
Adhering to a consistent style is crucial for code maintainability. These standards apply to all HTML, CSS, and JavaScript code interacting with HTMX.
### 1.1 HTML Formatting
* **Do This:** Use consistent indentation (2 spaces preferred). Attribute order should be logical (e.g., "id", "class", "hx-*", "other attributes").
* **Don't Do This:** Inconsistent indentation, random attribute order.
**Why:** Consistency improves readability and reduces visual noise. A logical attribute order contributes to faster code scanning.
**Example:**
"""html
<p>Click to load user profile</p>
<p>Click to load user profile</p>
"""
### 1.2 CSS Formatting
* **Do This:** Use consistent indentation (2 spaces preferred). Organize CSS properties logically (e.g., box model, typography, visual appearance). Consider using a CSS preprocessor (Sass, Less) for maintainability.
* **Don't Do This:** Inconsistent indentation, random property order, overly long CSS files without clear structure.
**Why:** A structured CSS codebase is easier to navigate and modify. Preprocessors enable features like variables, nesting, and mixins, which improve maintainability.
**Example:**
"""css
/* Good */
#user-profile {
width: 300px;
margin-bottom: 10px;
font-family: sans-serif;
font-size: 16px;
background-color: #f0f0f0;
border: 1px solid #ccc;
}
/* Bad */
#user-profile {
font-size: 16px;
background-color: #f0f0f0;
border: 1px solid #ccc;
width: 300px;
margin-bottom: 10px;
font-family: sans-serif;
}
"""
### 1.3 JavaScript Formatting
* **Do This:** Use consistent indentation (2 spaces preferred). Follow a style guide (e.g., Airbnb, Google). Use ESLint and Prettier for automated formatting and linting.
* **Don't Do This:** Inconsistent indentation, lack of semicolons, global variable pollution, overly complex logic.
**Why:** A standardized JavaScript style improves readability and reduces errors. Linters and formatters automate the process and enforce consistency across the team.
**Example:**
"""javascript
// Good
function handleClick(event) {
event.preventDefault();
const target = event.target;
fetch('/api/users')
.then(response => response.json())
.then(data => {
console.log('Users:', data);
// Update DOM with data (using HTMX may be preferable)
});
}
// Bad
function handleClick(event) {
event.preventDefault()
const target = event.target
fetch('/api/users').then(response => response.json()).then(data => {
console.log('Users:', data)
})
}
"""
## 2. HTMX Attribute Conventions
HTMX relies heavily on HTML attributes for its functionality. Clear and consistent attribute usage is paramount.
### 2.1 Consistent Prefixing
* **Do This:** Always use the "hx-" prefix for all HTMX attributes.
* **Don't Do This:** Using custom, non-prefixed attributes for HTMX functionality.
**Why:** The "hx-" prefix clearly identifies HTMX-related attributes and prevents naming collisions with other libraries or custom attributes.
**Example:**
"""html
Add Comment
Add Comment
"""
### 2.2 Attribute Ordering
* **Do This:** Place HTMX attributes after "id" and "class", and before other custom attributes. A suggested order is: "id", "class", "hx-get/post/put/delete", "hx-trigger", "hx-target", "hx-swap", "hx-other", "other data attributes".
* **Don't Do This:** Scattering HTMX attributes randomly throughout the element.
**Why:** Logical attribute ordering improves readability and helps quickly identify the core HTMX behavior of an element.
**Example:**
"""html
"""
### 2.3 Avoiding Redundancy
* **Do This:** Leverage HTMX's features to minimize redundant code. Use "hx-include" to include data from multiple form fields. Use "hx-vals" to pass fixed values. Utilize CSS classes for styling instead of inline styles that can hinder HTMX updates.
* **Don't Do This:** Manually constructing data payloads in JavaScript when HTMX attributes can handle it, using inline styles that will be overwritten with new content when HTMX performs a swap.
**Why:** Reducing redundancy simplifies the code and makes it easier to maintain.
**Example:**
"""html
Submit
Submit
"""
**Example:** "hx-vals" is a newer feature that allows passing fixed values to the server:
"""html
Mark as Pending
"""
### 2.4 Proper Use of "hx-swap"
* **Do This:** Choose the "hx-swap" strategy that best suits the update being performed. Understand the differences between "innerHTML", "outerHTML", "beforeend", "afterbegin", etc. Consider using "morph:" swaps for smoother transitions. Use "hx-swap="none"" when only side effects are needed and no DOM update is required.
* **Don't Do This:** Blindly using "innerHTML" for all updates, ignoring potential performance and security implications (e.g., XSS).
**Why:** Selecting the appropriate "hx-swap" strategy optimizes performance and prevents unexpected behavior. "morph:" swaps, when appropriate, provide a better user experience. "hx-swap="none"" prevents unnecessary re-renders.
**Example:**
"""html
Existing Comment
Add Comment
<p>Old Content</p>
Load New Content
<p>Old Content</p>
Load New Content
"""
### 2.5 Use of "hx-confirm"
* **Do This:** Use "hx-confirm" for potentially destructive actions like deleting data, especially those triggered with short click events, to offer the user a confirmation dialog.
* **Don't Do This:** Omitting confirmation dialogs for important destructive operatiions.
**Why:** Prevent accidental execution of important destructive client-side requests.
**Example:**
"""html
Delete Account
Delete Account
"""
## 3. Naming Conventions
Consistent naming makes code easier to understand and maintain.
### 3.1 Element IDs
* **Do This:** Use descriptive and meaningful IDs. Use camelCase or kebab-case for IDs. Choose ONE and stick to it.
* **Don't Do This:** Generic IDs like "button1", "div2".
**Why:** Descriptive IDs make it easier to understand the purpose of an element and its role in the HTMX application.
**Example:**
"""html
...
...
...
"""
### 3.2 CSS Classes
* **Do This:** Use a consistent naming convention (e.g., BEM, SUIT CSS, or a similar methodology) to create modular and reusable CSS classes.
* **Don't Do This:** Long, unorganized CSS class names.
**Why:** A well-defined CSS naming convention promotes maintainability and prevents naming collisions.
**Example (BEM):**
"""html
...
<p>...</p>
...
<p>...</p>
"""
### 3.3 HTMX Trigger Names
* **Do This:** Use descriptive trigger names, especially for custom events. Prefix custom event names to avoid conflicts.
* **Don't Do This:** Generic or ambiguous trigger names.
**Why:** Clear trigger names enhance code understandability and prevent unexpected behavior.
**Example:**
"""html
Load Data
Load Data
"""
## 4. Error Handling
Robust error handling is crucial for a reliable HTMX application.
### 4.1 Client-Side Error Handling
* **Do This:** Use "hx-on-*" attributes (introduced in newer HTMX versions) to handle events, including errors. Display user-friendly error messages in the UI, taking advantage of "hx-ext="response-targets"".
* **Don't Do This:** Ignoring potential errors, displaying raw error messages to users.
**Why:** Proper error handling ensures a graceful user experience and helps diagnose issues.
**Example (Using "hx-on" and "response-targets" extension):**
"""html
login
"""
### 4.2 Server-Side Error Handling
* **Do This:** Return appropriate HTTP status codes (e.g., 400, 404, 500) to indicate errors. Include informative error messages in the response body (e.g., in JSON format).
* **Don't Do This:** Returning 200 OK for all requests, even when errors occur; sending vague or unhelpful error messages.
**Why:** HTTP status codes allow HTMX to correctly identify and handle errors. Informative error messages help developers debug issues.
**Example (Server-side - Python/Flask):**
"""python
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/api/data', methods=['GET'])
def get_data():
try:
# Simulate an error
raise ValueError("Simulated error")
#return jsonify({'data': 'Some data'})
except ValueError as e:
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(debug=True)
"""
## 5. Performance Optimization
Optimizing performance is crucial for a responsive HTMX application.
### 5.1 Minimizing DOM Updates
* **Do This:** Target only the necessary parts of the DOM with "hx-target". Use the most efficient "hx-swap" strategy. Avoid unnecessary re-renders. Leverage "hx-select" to extract the relevant content from responses.
* **Don't Do This:** Targeting large sections of the DOM unnecessarily, using "innerHTML" for updates when a more specific swap strategy would suffice.
**Why:** Minimizing DOM updates reduces browser overhead and improves responsiveness.
**Example:**
"""html
Initial Data
Load Data
Initial Data
Load Data
"""
### 5.2 Caching
* **Do This:** Implement server-side caching to reduce database load. Use browser caching (e.g., "Cache-Control" headers) for static assets.
* **Don't Do This:** Fetching the same data repeatedly without caching.
**Why:** Caching reduces server load and improves response times.
### 5.3 Network Optimization
* **Do This:** Minimize the size of HTTP responses (e.g., by compressing content). Use a CDN for static assets. Optimize images. Consider using HTTP/2 or HTTP/3.
* **Don't Do This:** Sending large, uncompressed responses over slow network connections.
**Why:** Reducing network latency improves the perceived performance of the application.
### 5.4 Utilizing "hx-boost" Wisely
* **Do This:** Use "hx-boost" on "" and "" tags to automatically convert them into HTMX requests. Be mindful of its impact on SEO (ensure server-side rendering or a fallback mechanism for search engines).
* **Don't Do This:** Applying "hx-boost" indiscriminately to all links and forms without considering the potential consequences.
**Why:** "hx-boost" reduces the amount of code needed and simplifies the update of content.
**Example:**
"""html
Users
Search
"""
## 6. Security Considerations
Security is paramount. HTMX, like any web technology, requires careful attention to security best practices.
### 6.1 Preventing XSS
* **Do This:** Sanitize user input on the server-side before rendering it in HTML. Use templating engines with automatic escaping. Be cautious when using "innerHTML" or "outerHTML" with untrusted data. "morph:" swaps are generally safer in this regard.
* **Don't Do This:** Directly injecting user input into HTML without sanitization.
**Why:** Preventing XSS vulnerabilities protects users from malicious scripts injected into the application.
### 6.2 CSRF Protection
* **Do This:** Implement CSRF protection on all state-changing endpoints (POST, PUT, DELETE). Use a CSRF token in forms and AJAX requests. HTMX automatically handles CSRF tokens when posting forms.
* **Don't Do This:** Disabling CSRF protection without a valid reason.
**Why:** CSRF protection prevents attackers from forging requests on behalf of authenticated users.
### 6.3 Input Validation
* **Do This:** Validate all user input on both the client-side and the server-side. Enforce data type, length, and format constraints.
* **Don't Do This:** Trusting user input without validation.
**Why:** Input validation prevents data corruption and security vulnerabilities.
### 6.4 HTTPS
* **Do This:** Always use HTTPS to encrypt communication between the client and the server. Redirect HTTP requests to HTTPS.
* **Don't Do This:** Serving sensitive content over HTTP.
**Why:** HTTPS protects data in transit from eavesdropping and tampering.
## 7. Testing
* **Do This:** Write unit tests, integration tests, and end-to-end tests to ensure the HTMX application functions correctly. Use a testing framework (e.g., Jest, Mocha, Cypress).
* **Don't Do This:** Skipping testing or relying solely on manual testing.
**Why:** Testing helps prevent bugs and ensures that the application meets its requirements.
While HTMX itself might require less JavaScript, server-side code handling HTMX requests still needs comprehensive testing. Testing the integration between HTMX and the server is particularly important. For example, rendering using Jinja server-side (Python):
"""python
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
return render_template_string('''
Hello, HTMX!
Load New Content
''')
@app.route('/new_content')
def new_content():
return "New Content Loaded!"
if __name__ == '__main__':
app.run(debug=True)
"""
## 8. Modern HTMX Patterns and Techniques
* **Progressive Enhancement:** Build core functionality with standard HTML first, and then add HTMX to progressively enhance the user experience.
* **Server-Sent Events (SSE):** Use SSE with HTMX to create near-real-time updates in the UI. The "event" attribute can be used to listen to SSE events
"""html
Waiting for updates...
"""
* **WebSockets:** Integrate HTMX with WebSockets for full-duplex communication. Trigger changes based on messages received.
"""html
Waiting for WebSocket data...
"""
* **Custom Extensions:** Create custom HTMX extensions to encapsulate reusable logic and functionality. This is often a better approach than very long "hx-on" handlers.
"""javascript
htmx.defineExtension('my-extension', {
onEvent: function(name, evt) {
if (name === 'htmx:beforeRequest') {
console.log("About to make a request!");
}
}
});
"""
* **Use of "morph:" swaps:** Modern browsers implement optimized DOM diffing algorithms. Consider using "morph:" to reduce code and improve UX.
"""html
Change Me
Load New Content
"""
The response from "/new-content" should contain new HTML for parent-div.
* **Careful use of hx-boost:** While hx-boost can be very beneficial for initial development but remember that it effectively converts all links and forms to AJAX requests, which can cause issues with SEO and also history management. Use with caution!
By adhering to these code style and conventions standards, development teams can create cleaner, more maintainable, and more secure HTMX applications.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Testing Methodologies Standards for HTMX This document outlines the testing methodologies standards for HTMX applications, ensuring maintainability, reliability, and robustness. It covers unit, integration, and end-to-end testing strategies tailored for HTMX's unique characteristics. ## 1. Overview of HTMX Testing HTMX enables dynamic UI updates using standard HTML attributes. Testing HTMX applications requires considering both the server-side logic and the client-side behavior triggered by HTMX attributes. A comprehensive testing strategy combines unit tests for individual components, integration tests for interactions between client and server, and end-to-end tests for overall system functionality. ## 2. Unit Testing Unit tests focus on individual functions, classes, or components in isolation. For HTMX, this typically involves testing server-side handlers that generate HTML fragments and client-side JavaScript (if any). ### 2.1. Testing Server-Side Handlers Server-side handlers are responsible for generating HTML fragments based on HTMX requests. Unit tests should verify that these handlers produce the correct HTML and set appropriate HTTP headers. **Standard:** * **Do This:** Write unit tests for all server-side handlers that return HTML fragments for HTMX requests. * **Don't Do This:** Neglect server-side testing; ensure that HTML fragments are validated. **Why:** * Ensures that HTML fragments are correctly generated, reducing bugs. * Provides immediate feedback on code changes, improving development efficiency. **Example (Python/Flask):** """python from flask import Flask, request, render_template import unittest app = Flask(__name__) @app.route('/update') def update(): item_id = request.args.get('id') return render_template('item.html', item_id=item_id) class TestApp(unittest.TestCase): def setUp(self): app.testing = True self.app = app.test_client() def test_update_route(self): response = self.app.get('/update?id=123') self.assertEqual(response.status_code, 200) self.assertIn(b'Item ID: 123', response.data) # item.html # <div id="item-123">Item ID: {{ item_id }}</div> """ **Common Anti-Patterns:** * Testing only the status code without validating the HTML content. * Using string matching without accounting for HTML structure. ### 2.2. Testing Client-Side JavaScript (if applicable) If you have custom JavaScript that interacts with HTMX, unit tests are crucial. Use a JavaScript testing framework like Jest or Mocha. **
# Core Architecture Standards for HTMX This document outlines the core architectural standards for building robust, maintainable, and scalable web applications using HTMX. It focuses on fundamental patterns, project structure, and organization principles, specifically within the context of HTMX's unique approach to front-end development. These standards are based on the latest version of HTMX and promote modern best practices. ## 1. Project Structure and Organization A well-defined project structure is crucial for maintainability and collaboration. This section defines the recommended file organization and component structure for HTMX projects. ### 1.1 Directory Structure **Do This:** Adopt a feature-oriented directory structure. Group related files (HTML templates, CSS, JavaScript – if needed - server-side logic) within feature-specific directories. **Don't Do This:** Create a structure based solely on file types (e.g., a single 'templates' directory, a single 'css' directory). This leads to scattering related code across multiple directories and makes features harder to locate and maintain. **Why:** Feature-oriented structure improves code discoverability and reduces cognitive load for developers. When a feature needs modification, all relevant files are located together. **Example:** """ my-project/ ├── backend/ # Server-side code (e.g., Python/Flask, Node.js/Express, etc.) ├── frontend/ # HTMX Specific files. This directory is served staticly │ ├── components/ # Reusable UI components │ │ ├── button/ │ │ │ ├── button.html # Template fragment │ │ │ ├── button.css # Styles (optional) │ │ │ └── button.js # Vanilla JS logic (only if absolutely necessary) │ │ ├── modal/ │ │ │ ├── modal.html │ │ │ └── modal.css │ ├── pages/ # Complete page templates │ │ ├── home.html │ │ ├── about.html │ ├── styles/ # Global CSS │ │ ├── main.css │ ├── scripts/ # Global Javascript (Use with caution) │ │ ├── main.js │ ├── images/ # Static Images if necessary. Prefer using base64 in HTML or remote URLs. │ ├── htmx.min.js # HTMX Library │ └── index.html # Main application entry-point / layout (if applicable) ├── README.md └── requirements.txt # Python dependencies (example) """ ### 1.2 Component-Based Architecture **Do This:** Design your UI as a collection of reusable components. Each component should encapsulate a specific piece of functionality (e.g., a search bar, a product card, a modal). **Don't Do This:** Create monolithic HTML pages with tightly coupled logic. This results in code duplication and makes it harder to maintain and reuse parts of the UI. **Why:** Component-based design promotes modularity, reusability, and testability. Components can be easily composed to build complex UIs, and changes to one component are less likely to affect other parts of the application. HTMX works perfectly with component-based design. **Example:** A reusable button component: """html <!-- frontend/components/button/button.html --> <button class="btn btn-primary" hx-get="{{ url }}" hx-target="{{ target }}" hx-swap="{{ swap }}"> {{ text }} </button> """ Usage in a page: """html <!-- frontend/pages/home.html --> <!DOCTYPE html> <html> <head> <title>Home</title> <link rel="stylesheet" href="/styles/main.css"> <script src="/htmx.min.js"></script> </head> <body> <h1>Welcome!</h1> {% include "components/button/button.html" with context={"url": "/load-data", "target": "#data-container", "swap": "innerHTML", "text": "Load Data"} %} <div id="data-container"> <!-- Data will be loaded here --> </div> </body> </html> """ ### 1.3 Separation of Concerns **Do This:** Keep HTML templates focused on structure and presentation. Move complex logic to the server-side, or, *only when necessary* to small, well-contained JavaScript modules. **Don't Do This:** Embed large amounts of JavaScript directly within HTML templates or overuse inline styles. **Why:** Separation of concerns improves code readability, maintainability, and testability. It also makes it easier to change the presentation without affecting the application's logic and vice versa. HTMX strongly encourages leveraging server-side rendering for the majority of the application's logic. **Example:** Correct Separation of Concerns: """html <!-- frontend/components/product-card/product-card.html --> <div class="product-card"> <h2>{{ product.name }}</h2> <p>{{ product.description }}</p> <button hx-post="/add-to-cart" hx-target="#cart-items" hx-swap="beforeend" hx-vals='{"product_id": "{{ product.id }}"}'> Add to Cart </button> </div> """ Anti-Pattern (Mixing presentation and excessive JavaScript): """html <!-- frontend/components/product-card/product-card.html (BAD) --> <div class="product-card"> <h2>{{ product.name }}</h2> <p>{{ product.description }}</p> <button onclick="addToCart('{{ product.id }}')">Add to Cart</button> </div> <script> function addToCart(productId) { // Complex logic here...Avoid please. fetch('/add-to-cart', { method: 'POST', body: JSON.stringify({product_id: productId}), headers: {'Content-Type': 'application/json'} }) .then(response => response.text()) .then(data => { document.getElementById('cart-items').insertAdjacentHTML('beforeend', data); }); } </script> """ ### 1.4 Single Responsibility Principle (SRP) with HTMX **Do This:** Ensure that each HTML component, especially those targeted by HTMX requests, has a *single, well-defined reason to change*. This directly relates to the component's responsibility within the application. **Don't Do This:** Create "god components" that handle multiple unrelated functions or display diverse data sets. This leads to complex templates, bloated server-side code, and increased difficulty in maintaining the application. **Why:** Adhering to SRP improves the modularity, readability, and maintainability of your HTMX application. It makes testing easier, reduces the impact of changes, and improves the reusability of components. **Example:** Imagine a component that displays user information and also handles user deletion. This violates SRP. Instead, split it into two components: one for displaying user details and another (likely a button or link) for initiating the delete action. **Good:** """html <!-- frontend/components/user-details.html --> <div id="user-{{user.id}}"> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> <!-- Delete User component will go here --> </div> """ """html <!-- frontend/components/delete-user-button.html --> <button hx-delete="/users/{{user.id}}" hx-target="#user-{{user.id}}" hx-swap="outerHTML" class="btn btn-danger"> Delete User </button> """ **Anti-Pattern (Violates SRP):** """html <!-- frontend/components/user.html (BAD) --> <div id="user-{{user.id}}"> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> <button hx-delete="/users/{{user.id}}" hx-target="#user-{{user.id}}" hx-swap="outerHTML" class="btn btn-danger"> Delete User </button> {% if show_admin_options %} <button hx-get="/users/{{user.id}}/edit" hx-target="#user-{{user.id}}" hx-swap="outerHTML">Edit User</button> {% endif %} </div> """ In the anti-pattern, the component handles both displaying user information and potentially admin-related actions like editing, depending on the "show_admin_options" variable. This combines different responsibilities into a single component, making it more complex and harder to test. The better approach is to separate the admin-related actions into distinct components, conditionally included on the page based on the user's role. ## 2. Architectural Patterns for HTMX This Section outlines common architectural patterns for creating Single Page Applications (SPAs) and Multi-Page Applications using HTMX. ### 2.1 Progressive Enhancement **Do This:** Build your application with standard HTML forms and links first. Then, use HTMX to progressively enhance the user experience by adding AJAX functionality without requiring a full page reload. **Don't Do This:** Rely *solely* on HTMX from the start, creating an application that is unusable without JavaScript. **Why:** Progressive enhancement ensures that your application is accessible to users with JavaScript disabled or limited network connectivity. It also provides a solid foundation for search engine optimization (SEO). **Example:** Standard HTML form (works without JavaScript): """html <form action="/search" method="GET"> <input type="text" name="query" id="search-input" placeholder="Search..."> <button type="submit">Search</button> </form> <div id="search-results"> <!-- Search results will be displayed here --> </div> """ Progressively enhanced with HTMX: """html <form action="/search" method="GET" hx-get="/search" hx-target="#search-results" hx-indicator="#search-indicator"> <input type="text" name="query" id="search-input" placeholder="Search..."> <button type="submit">Search</button> <img id="search-indicator" class="htmx-indicator" src="/images/spinner.gif"> </form> <div id="search-results"> <!-- Search results will be displayed here --> </div> """ In this example the submit button will do the default browser behavior if javascript is disabled. With javascript enabled, the HTMX form will intercept the form, and do an AJAX request. ### 2.2 Server-Driven UI **Do This:** Embrace server-side rendering for most of your UI. Use HTMX to fetch and update small fragments of HTML from the server in response to user interactions. **Don't Do This:** Create a client-side JavaScript application and use HTMX only for data fetching. If you're doing this, you're likely missing the point of HTMX and should re-evaluate your architectural choices. **Why:** Server-driven UI simplifies development, improves initial page load performance, and reduces the amount of JavaScript required. It also leverages the server's resources for rendering and data processing. **Example** Implement a simple counter. """html <!-- frontend/pages/counter.html --> <!DOCTYPE html> <html> <head> <title>Counter</title> <script src="/htmx.min.js"></script> </head> <body> <h1>Counter</h1> <div id="counter"> {{ count }} </div> <button hx-post="/increment" hx-target="#counter" hx-swap="innerHTML">+</button> </body> </html> """ Server-side (Python/Flask Example): """python # backend/app.py from flask import Flask, render_template, request app = Flask(__name__) count = 0 @app.route('/') def index(): global count return render_template('counter.html', count=count) @app.route('/increment', methods=['POST']) def increment(): global count count += 1 return str(count) # Return just the updated count if __name__ == '__main__': app.run(debug=True) """ **Explanation:** The browser renders the initial HTML (including "htmx.min.js"). When the user clicks the "+" button: 1. **HTMX Intercepts:** HTMX intercepts the click event. 2. **AJAX Request:** HTMX performs a "POST" request to "/increment". 3. **Server-Side Logic:** The Flask server increments the count and returns the new count as plain text. 4. **HTML Update:** HTMX receives the response (e.g., "1", "2", etc.) and updates the content of the "#counter" element; by replacing the old text fragment by the new one fetched from the server. ### 2.3 Template Fragments and Composition **Do This:** Break down your UI into small, reusable template fragments. Use HTMX to fetch and compose these fragments dynamically to build complete pages or update existing content. **Don't Do This:** Return entire HTML pages for every request, even for small updates. This leads to unnecessary data transfer and slower response times. **Why:** Template fragments improve code reusability, reduce code duplication, and make it easier to update specific parts of the UI without reloading the entire page. **Example:** """html <!-- backend/templates/partials/product-list.html --> <ul> {% for product in products %} <li>{{ product.name }} - ${{ product.price }}</li> {% endfor %} </ul> """ """html <!-- backend/templates/pages/home.html --> <!DOCTYPE html> <html> <head> <title>Home</title> <script src="/htmx.min.js"></script> </head> <body> <h1>Products</h1> <div id="product-list"> {% include 'partials/product-list.html' %} </div> <button hx-get="/load-more-products" hx-target="#product-list" hx-swap="beforeend">Load More</button> </body> </html> """ ### 2.4 Utilizing HTMX Extensions **Do This:** Explore and utilize provided HTMX extensions (e.g., "morphdom", "ajax-header", "client-side-templates"). These extensions provide additional functionality and can simplify your code. Use them judiciously; don't add dependencies unless necessary. **Don't Do This:** Re-implement functionality that is already provided by HTMX extensions. **Why:** Extensions allow extending HTMX behavior in a declarative and maintainable way. They follow coding best practices and are tested by a wider community. **Example:** Using the "morphdom" extension: 1. Include extension """html <script src="https://unpkg.com/htmx.org/dist/ext/morphdom.js"></script> """ 2. Use the "morph:" modifier """html <div id="my-div"> <p>Some content</p> </div> <button hx-get="/new-content" hx-swap="morph:#my-div">Load New Content</button> """ The "morphdom" extension intelligently updates the DOM, preserving focus and state, leading to smoother transitions. ### 2.5 Idempotency and Safe Methods **Do This:** Ensure that your server-side endpoints are idempotent, especially those handling "POST", "PUT", and "DELETE" requests. The "GET" method has to be always idempotent, and should never change any state. Ideally, any endpoint that modify state should implement the POST-Redirect-GET pattern **Don't Do This:** Perform non-idempotent operations on "GET" requests. **Why:** Idempotency ensures that repeated requests have the SAME effect, preventing unintended side effects (e.g., duplicate orders, multiple deletions). HTMX, like any AJAX library, might retry requests under certain conditions (e.g., network errors). **Example:** """python # backend/app.py (Python/Flask Example) from flask import Flask, request, redirect, url_for app = Flask(__name__) items = {} # In-memory store (for demonstration purposes) next_item_id = 1 @app.route('/items', methods=['POST']) def create_item(): global next_item_id, items item_name = request.form.get('name') items[next_item_id] = item_name item_id = next_item_id next_item_id += 1 # POST-Redirect-GET pattern return redirect(url_for('get_items')) @app.route('/items', methods=['GET']) def get_items(): item_list = "<ul>" for id, name in items.items(): item_list += f"<li>{name} (ID: {id})</li>" item_list += "</ul>" return item_list if __name__ == '__main__': app.run(debug=True) """ In this example, creating an item creates it in the backend. The get items will get/display all items. ## 3. HTMX Specific Code Standards This section focuses specifically on HTMX attribute implementation best practices and details. ### 3.1 Consistent Attribute Usage **Do This:** Use consistent attribute naming and values throughout your application. Establish a standard for common HTMX attributes (e.g., "hx-target", "hx-swap", "hx-trigger"). **Don't Do This:** Use inconsistent attribute names or values across different parts of the application. This leads to confusion and makes it harder to maintain the code. **Why:** Consistency improves code readability and reduces the likelihood of errors. Enforce consistency through code reviews or linters. **Example:** Consistent "hx-swap" values: """html <button hx-post="/update" hx-target="#my-element" hx-swap="innerHTML">Update</button> <button hx-post="/delete" hx-target="#my-element" hx-swap="outerHTML">Delete</button> """ ### 3.2 "hx-vals" for Data Transmission **Do This:** Use the "hx-vals" attribute to send additional data to the server along with the HTMX request. This is especially useful for passing context or parameters required by your server-side logic. Use JSON format for complex data. **Don't Do This:** Rely solely on URL parameters or hidden form fields for transmitting data, especially for complex data structures. **Why:** "hx-vals" provides a clean and declarative way to associate data with an HTMX request. It promotes better organization and reduces the risk of errors. **Example:** """html <button hx-post="/update-item" hx-target="#item-details" hx-swap="innerHTML" hx-vals='{"item_id": 123, "quantity": 5}'> Update Item </button> """ ### 3.3 Targeting Strategies **Do This:** Choose the appropriate "hx-target" strategy based on the desired update behavior. Use CSS selectors for precise targeting of elements within the DOM. Keep selectors as specific as possible to avoid unexpected updates elsewhere. **Don't Do This:** Use overly broad CSS selectors that might inadvertently target unintended elements. Avoid targeting the "<body>" element unless you intend to replace the entire page content. **Why:** Precise targeting prevents unexpected UI updates and ensures that only the intended elements are modified. **Example:** Targeting a specific element by ID: """html <div id="my-container"> <h2>Title</h2> <p id="content">Some content</p> </div> <button hx-get="/load-new-content" hx-target="#content" hx-swap="innerHTML">Load New Content</button> """ ### 3.4 Swap Modifiers **Do This:** Effectively use swap modifiers (e.g. "beforeend", "afterend", "morph") to control how HTMX updates the DOM,. **Don't Do This:** Default to "innerHTML" blindly. Evaluate swapping strategies for optimal performance. **Why:** Choosing the right swap option ensures optimal UI updates, improves performance, and reduces the likelihood of unexpected side-effects. **Example:** """html <!-- Using afterend to insert content after an element --> <ul id="my-list"> <li>Item 1</li> <li>Item 2</li> </ul> <button hx-get="/add-item" hx-target="#my-list" hx-swap="beforeend">Add Item</button> <!-- Using morph to perform a smart diff of the new HTML and the old HTML --> <script src="https://unpkg.com/htmx.org/dist/ext/morphdom.js"></script> <div id="my-element"> <p>Some Text</p> </div> <button hx-get="/get_new_element" hx-swap="morph:#my-element /> """ ### 3.5 Trigger Management **Do This:** Employ a consistent and well-documented approach to event triggering. Use modifiers like "once", "throttle", and "debounce" to control the frequency of requests. Use "hx-trigger" effectively. **Don't Do This:** Overuse polling or create unnecessary request traffic **Why:** Correct trigger improves application performance and reduces server load. **Example:** """html <!-- Throttle a search request to be sent at most once every 200ms --> <input type="text" hx-get="/search" hx-target="#results" hx-trigger="keyup changed delay:200ms" name="q"> <!-- Trigger an event once --> <button hx-get="/login" hx-target="#login-form" hx-trigger="once">Login</button> """ ## 4. Performance Considerations Building performant applications is important. This rule helps deliver great experience. ### 4.1 Minimize DOM Updates **Do This:** Target the smallest possible DOM element that needs updating, and use the most efficient "hx-swap" strategy for the update use "outerHTML swap:none" to avoid the "double request problem with hx-boost. **Don't Do This:** Update large sections of the DOM unnecessarily, or use inefficient "hx-swap" strategies like "innerHTML" when "outerHTML" or "morph" would suffice. **Why:** Minimizing DOM updates reduces browser reflow and repaint operations, which can significantly improve performance, especially on complex pages. **Example:** Instead of replacing an entire list: """html <ul id="item-list"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <button hx-post="/add-item" hx-target="#item-list" hx-swap="innerHTML">Add Item</button> <!-- Inefficient --> """ Append only the new item: """html <ul id="item-list"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <button hx-post="/add-item" hx-target="#item-list" hx-swap="beforeend">Add Item</button> <!-- Efficient --> """ ### 4.2 Optimize Server Responses **Do This:** Ensure that your server generates HTML fragments quickly. Use caching strategies where appropriate to reduce database load and response times. Compress responses with Gzip or Brotli. **Don't Do This:** Generate HTML fragments slowly on the server, or neglect to cache frequently accessed data. **Why:** Slow server responses directly translate to a sluggish user experience. Optimizing server-side performance is critical for HTMX applications. ### 4.3 Lazy Loading and Pagination **Do This:** Implement lazy loading for images and other resources that are not immediately visible on the page. Use pagination to break up large datasets into smaller chunks. Add "hx-boost" to HTML elements, to persist them across pages. **Don't Do This:** Load all resources upfront, or display large datasets on a single page without pagination. **Why:** Lazy loading and pagination improve initial page load performance and reduce the amount of data transferred over the network. ### 4.4 Connection Pooling **Do This:** In backend implementations, ensure connection pooling is used to speed up database calls (if any) and/or connections to other downstream systems **Don't Do This:** Reestablish connections for every call. This is hugely inefficient. **Why:** Connection pools removes the overhead of establishing connections, improving performance significantly. """python # backend/app.py (Python/Flask Example with SQLAlchemy connection pooling) from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@host:port/database' app.config['SQLALCHEMY_POOL_SIZE'] = 5 # Number of connections in the pool app.config['SQLALCHEMY_MAX_OVERFLOW'] = 10 # Maximum overflow connections db = SQLAlchemy(app) # ... rest of your application code ... """ ## 5. Security Considerations It is important to follow best practices of Secure Development. ### 5.1 Input Validation and Output Encoding **Do This:** Validate all user input on the server-side to prevent injection attacks (e.g., XSS, SQL injection). Encode output properly before rendering it in HTML templates. **Don't Do This:** Trust user input blindly, or neglect to encode output properly. **Why:** Input validation and output encoding are essential for preventing security vulnerabilities. **Example:** Safe rendering, escaping HTML entities. """python # backend/app.py from flask import Flask, request, render_template, escape app = Flask(__name__) @app.route('/search') def search(): query = request.args.get('query') # Escape the user-provided query safe_query = escape(query) results = search_database(query) return render_template('search_results.html', query=safe_query, results=results) """ """html <!-- backend/templates/search_results.html --> <h1>Search Results for "{{ query }}"</h1> <!-- 'query' is already escaped --> <ul> {% for result in results %} <li>{{ result.title }}</li> {% endfor %} </ul> """ ### 5.2 CSRF Protection **Do This:** Implement CSRF (Cross-Site Request Forgery) protection for all state-changing requests. If a backend framework doesn't provide it, HTMX ships with the "ext/ajax-header" extension that simplifies this. Consider using appropriate headers to protect calls. **Don't Do This:** Disable CSRF protection, or rely solely on client-side validation. **Why:** CSRF protection prevents attackers from forging requests on behalf of authorized users. It also makes it easier to protect requests on the server. ### 5.3 Follow security best practices **Do This:** Always follow OWASP (Open Web Application Security Project) guidelines for security. **Don't Do This:** Disregard common known security mistakes, like keeping secrets on the front end. **Why:** Security best practices are well researched/vetted for security flaws. ## 6. Error Handling A well structured HTMX application will properly handle error conditions. ### 6.1 Specific Error Messages. **Do This:** Return specific and actionable error messages. **Don't Do This:** Return overly broad and not descriptive error messages. **Why:** Specific errors help when debugging. ### 6.2 Use hx-ext for error handling patterns **Do This:** Use the "hx-ext" for custom or shared error/alerting in HTMX **Don't Do This:** Reinvent the wheel and add more complex Javascript. **Why:** "hx-ext" extensions allow the extensions of HTMX, without lots of boilerplate code.
# Component Design Standards for HTMX This document outlines best practices for designing reusable, maintainable components within HTMX applications. It emphasizes modern approaches and patterns, specifically tailored to leverage HTMX's capabilities and avoid common pitfalls. Adhering to these standards will promote code clarity, reduce redundancy, and improve the overall quality of HTMX-based projects. ## 1. Component Architecture and Organization This section deals with how to structure your HTMX components and how they are organized to achieve reusability and maintainability. ### 1.1. Single Responsibility Principle **Standard:** Each HTMX component should have one, and only one, clear responsibility. **Do This:** Design components that encapsulate a specific piece of UI or functionality. For example, a "product-card" component responsible for displaying a product's information, or a "search-bar" component responsible to provide a search input and display related results. **Don't Do This:** Create monolithic components that handle multiple unrelated concerns, such as displaying data and managing authentication. **Why:** The Single Responsibility Principle promotes modularity and makes components easier to understand, test, and modify. Changes to one component are less likely to impact other parts of the application. **Example:** """html <!-- Good: Dedicated product card component --> <div id="product-123" hx-get="/product/123" hx-trigger="revealed" hx-swap="outerHTML"> <!-- Loading state (can be replaced by response) --> Loading... </div> <!-- Bad: Mixing product display with other logic --> <div id="product-area"> <!-- Display product details (potentially mixed with other unrelated elements) --> </div> """ ### 1.2. Composition over Inheritance **Standard:** Favor composition (combining multiple small components) over inheritance (creating deep component hierarchies). **Do This:** Build complex components from smaller, reusable building blocks. Use HTMX attributes to orchestrate interactions between these components. **Don't Do This:** Rely on deeply nested inheritance structures, as they often lead to tight coupling and increased complexity. HTMX generally discourages classical inheritance patterns as they're typically employed in component-based UI frameworks like React or Vue. **Why:** Composition provides greater flexibility and reduces the risk of the fragile base class problem. It allows for more targeted customization and reuse of components. **Example:** """html <!-- Product Card Component --> <div class="product-card"> <div class="product-image"> <img src="/images/product1.jpg" alt="Product 1"> </div> <div class="product-details"> <h3 class="product-name">Awesome Gadget</h3> <p class="product-description">A brief description of the product.</p> <button hx-post="/add-to-cart/123" hx-target="#cart-summary" hx-swap="outerHTML">Add to Cart</button> </div> </div> <!-- Cart Summary Component --> <div id="cart-summary" hx-get="/cart/summary" hx-trigger="revealed" hx-swap="innerHTML"> Loading cart summary... </div> """ ### 1.3. Component Naming Conventions **Standard:** Use clear, descriptive names for your components and their corresponding IDs and classes. **Do This:** Adopt a consistent naming convention, such as using kebab-case ("product-card", "search-bar") or BEM (Block Element Modifier) for class names. Example: "product", "product__image", "product__name". **Don't Do This:** Use vague or ambiguous names like "div1", "item", or generic class names like "style1", "content". **Why:** Clear naming improves code readability and makes it easier to understand the purpose of each component. **Example:** """html <!-- Good: Descriptive names --> <div id="product-list" class="product-list"> <div class="product-card">...</div> </div> <!-- Bad: Ambiguous names --> <div id="items" class="container"> <div class="item">...</div> </div> """ ### 1.4. Directory Structure **Standard:** Organize components into a logical directory structure that reflects their purpose and relationships. **Do This:** Create separate directories for different types of components (e.g., "components/", "pages/", "shared/"). Within each directory, place related component files together. **Don't Do This:** Dump all component files into a single directory, making it difficult to find and manage them. **Why:** A well-structured directory layout improves code organization and facilitates navigation within the project. **Example:** """ - project/ - components/ - product-card/ - product-card.html - product-card.css - search-bar/ - search-bar.html - search-bar.css - pages/ - home/ - home.html - shared/ - styles/ - base.css """ ## 2. Component Implementation Details This section covers the specifics of how to implement your HTMX components, focusing on leveraging HTMX attributes and backend integration. ### 2.1. HTMX Attribute Usage **Standard:** Use HTMX attributes effectively to define component behavior and interactions. **Do This:** Leverage HTMX attributes like "hx-get", "hx-post", "hx-trigger", "hx-target", and "hx-swap" to implement dynamic updates and interactions without writing JavaScript. Employ "hx-vals" to send additional data to the server. Use "hx-confirm" for destructive actions. **Don't Do This:** Overuse JavaScript when HTMX attributes can achieve the same result. Write complex JavaScript logic that duplicates HTMX's functionality. Neglect to sanitize user inputs or implement security measures to prevent injection attacks when handling HTMX requests. **Why:** HTMX promotes a declarative approach to UI development, reducing reliance on JavaScript and simplifying component logic. Using HTMX attributes effectively leads to more concise and maintainable code. **Example:** """html <!-- Fetch product details on click --> <button hx-get="/product/details/123" hx-target="#product-details" hx-swap="innerHTML"> Show Details </button> <!-- Trigger a request on input change. use lazy modifier to avoid too many requests --> <input type="text" name="search" hx-get="/search" hx-trigger="keyup changed delay:500ms" hx-target="#results" hx-swap="innerHTML"> """ ### 2.2. Server-Side Rendering (SSR) with HTMX **Standard:** Design components that can be rendered on the server to provide initial content and improve SEO. **Do This:** Ensure that your server-side code can generate the HTML for each component, including any necessary data. Use a templating engine (e.g., Jinja2, Django Templates, Go templates) to render components dynamically based on data from your backend. **Don't Do This:** Rely solely on client-side rendering, as this can negatively impact SEO and initial page load time. **Why:** Server-side rendering provides a better user experience and makes your application more accessible to search engines. HTMX complements SSR by enabling dynamic updates after the initial page load. **Example (Python/Flask with Jinja2):** """python from flask import Flask, render_template, request app = Flask(__name__) @app.route('/') def index(): products = [ {'id': 1, 'name': 'Product A', 'price': 29.99}, {'id': 2, 'name': 'Product B', 'price': 49.99} ] return render_template('index.html', products=products) @app.route('/product/<int:product_id>') def product_details(product_id): # Simulate fetching product details from a database product = {'id': product_id, 'name': f'Product {product_id}', 'description': 'Detailed description'} return render_template('product_details.html', product=product) if __name__ == '__main__': app.run(debug=True) """ """html <!-- templates/index.html --> <!DOCTYPE html> <html> <head> <title>Product List</title> </head> <body> <h1>Available Products</h1> <div id="product-list"> {% for product in products %} <div class="product-card"> <h3>{{ product.name }}</h3> <p>Price: ${{ product.price }}</p> <button hx-get="/product/{{ product.id }}" hx-target="#product-details" hx-swap="innerHTML"> Show Details </button> </div> {% endfor %} </div> <div id="product-details"> <!-- Product details will be loaded here --> </div> </body> </html> """ """html <!-- templates/product_details.html --> <div> <h3>{{ product.name }}</h3> <p>{{ product.description }}</p> </div> """ ### 2.3. Partial Updates **Standard:** Utilize partial updates to minimize data transfer and improve performance. **Do This:** Return only the necessary HTML fragments from the server, instead of re-rendering the entire page. Use "hx-target" and "hx-swap" to precisely control where and how the updates are applied. Leverage "hx-select" on the server-side response to pick only the relevant parts of the returned HTML. **Don't Do This:** Return large HTML payloads that include unchanged content. **Why:** Partial updates reduce network bandwidth consumption and improve UI responsiveness. **Example:** """html <!-- Update only the cart summary after adding an item --> <button hx-post="/add-to-cart/123" hx-target="#cart-summary" hx-swap="outerHTML">Add to Cart</button> <div id="cart-summary" hx-get="/cart/summary" hx-trigger="revealed" hx-swap="innerHTML"> Loading cart summary... </div> """ Suppose your server returns the following HTML from "/cart/summary": """html <div id="cart-summary"> <p>Items in cart: 3</p> <p>Total: $75.00</p> </div> """ ### 2.4. Component Styling **Standard:** Style components in a modular and maintainable way. **Do This:** Use CSS classes to style components, avoiding inline styles. Consider using CSS preprocessors like Sass or Less for increased maintainability. Follow a consistent naming convention (e.g., BEM) for CSS classes. Use CSS variables for theming and customization. **Don't Do This:** Use inline styles excessively. Create overly specific CSS selectors that are difficult to override. **Why:** Modular CSS promotes code reuse and makes it easier to maintain and update the visual appearance of your components. Good CSS practices are vital for long-term maintainability. **Example:** """html <!-- Good: Using CSS classes --> <div class="product-card"> <h3 class="product-card__name">Awesome Gadget</h3> <p class="product-card__price">$29.99</p> </div> """ """css /* product-card.css */ .product-card { border: 1px solid #ccc; padding: 10px; margin-bottom: 10px; } .product-card__name { font-size: 1.2em; margin-bottom: 5px; } .product-card__price { color: green; } """ ### 2.5. State Management **Standard:** Handle component state effectively, using HTMX and server-side state where appropriate. **Do This:** Prefer server-side state management for data that needs to be persisted or shared across multiple users. Use HTMX to update the UI based on changes in state. For local, transient state, use HTML5 data attributes or minimal JavaScript where absolutely necessary. Consider using cookies or local storage for client-side persistence of non-sensitive data. **Don't Do This:** Over-rely on client-side JavaScript for managing complex state, especially when the data is relevant to the server or other users. **Why:** HTMX is designed to minimize the need for complex client-side state management. Leveraging server-side state simplifies component logic and ensures data consistency. **Example:** """html <!-- Server-side state (e.g., like count in the database)--> <div id="counter" hx-get="/get-count" hx-trigger="revealed" hx-swap="innerHTML"> Loading... </div> <button hx-post="/increment-count" hx-target="#counter" hx-swap="innerHTML">Increment</button> """ """python # Server-side (Flask) from flask import Flask, session app = Flask(__name__) app.secret_key = 'some_secret' # VERY IMPORTANT FOR SESSION SECURITY, CHANGE THIS! @app.route('/get-count') def get_count(): if 'count' not in session: session['count'] = 0 return str(session['count']) @app.route('/increment-count', methods=['POST']) def increment_count(): if 'count' not in session: session['count'] = 0 session['count'] += 1 # Need to mark session as modified if not using a ORM like SQLAlchemy session.modified = True return str(session['count']) """ ### 2.6. Error Handling **Standard:** Implement robust error handling for HTMX requests. **Do This:** Provide informative error messages to the user when requests fail. Use "hx-on::response-error" to catch network errors. Return appropriate HTTP status codes (e.g., 400, 500) from the server to indicate the type of error. Display meaningful error messages in the UI. Implement logging on the server-side to capture and investigate errors. **Don't Do This:** Silently fail or display generic error messages that don't provide any context. Assume that all requests will succeed. **Why:** Proper error handling is crucial for providing a good user experience and ensuring the reliability of your application. **Example:** """html <button hx-post="/submit-form" hx-target="#form-result" hx-swap="innerHTML" hx-on::response-error="alert('An error occurred!')">Submit</button> <div id="form-result"></div> """ """python # Server-side (Flask) from flask import Flask, jsonify app = Flask(__name__) @app.route('/submit-form', methods=['POST']) def submit_form(): # Simulate error condition if True: # Replace this with actual validation or error check return jsonify({'error': 'Invalid input'}), 400 return "Form submitted successfully", 200 """ ### 2.7 Security **Standard:** When creating HTMX components, security must be considered at every stage. **Do This:** Sanitize and validate all incoming user inputs, whether they come from forms or other sources, using libraries like DOMPurify. Set the "Content-Security-Policy(CSP)" header to mitigate XSS attacks, carefully whitelisting allowed sources. For forms, implement CSRF protection (often handled by the backend framework). When making API calls, implement proper authentication and authorization mechanisms to protect sensitive features. Whenever possible, use HTTPS to encrypt data in transit. **Don't Do This:** Trust user-provided data without proper sanitization! Neglecting input sanitization leads to XSS vulnerabilities. Avoid using "unsafe-inline" in the CSP as it opens dangerous avenues for attack. Do not commit secrets to the client or hardcode credentials in the source code. **Why:** Security vulnerabilities can have drastic consequences, costing money and damaging reputation. Good security practices should be a primary focus when building HTMX components. **Example:** """html <form hx-post="/comment" hx-target="#comments" hx-swap="beforeend"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <textarea name="text"></textarea> <button>Submit</button> </form> """ """python # Server-side (Flask) from flask import Flask, render_template, request, session import os import secrets app = Flask(__name__) app.secret_key = secrets.token_hex(16) # Generate a strong secret key def generate_csrf_token(): if 'csrf_token' not in session: session['csrf_token'] = secrets.token_hex(16) return session['csrf_token'] app.jinja_env.globals['csrf_token'] = generate_csrf_token @app.route('/comment', methods=['POST']) def post_comment(): csrf_token = request.form.get('csrf_token') if not csrf_token or csrf_token != session.get('csrf_token'): return "CSRF token is invalid", 400 comment_text = request.form.get('text') # TODO: Sanitize comment_text! # comment_text = some_sanitization_function(comment_text) # Store in database, etc. return f"<p>{comment_text}</p>" """ ### 2.8 Accessibility **Standard:** HTMX components should adhere to accessibility guidelines (WCAG) to ensure inclusivity. **Do This:** Use semantic HTML elements appropriately (e.g., "<nav>", "<article>", "<aside>"). Provide alternative text for images ("alt" attribute). Ensure sufficient color contrast. Make keyboard navigation functional with "tabindex" attributes. Provide ARIA attributes to enhance accessibility where necessary (e.g., "aria-label", "aria-describedby"). **Don't Do This:** Rely solely on visual cues. Neglect keyboard navigation support. Ignore accessibility testing. **Why:** Accessibility is essential for making your application usable by everyone, including users with disabilities. **Example:** """html <img src="product.jpg" alt="Image of the product" /> <button aria-label="Add to cart">Add to Cart</button> """ ## 3. Advanced Component Patterns These are patterns for more complex scenarios that are important for building responsive and intuitive interfaces with HTMX. ### 3.1. Progressive Enhancement **Standard:** Embrace progressive enhancement by building components that work even without JavaScript enabled. **Do This:** Ensure that basic functionality is available without HTMX. Use HTMX to enhance the user experience with dynamic updates and interactions when JavaScript is enabled. Design components to be graceful in the face of network failures or slow connections. **Don't Do This:** Create components that are completely dependent on JavaScript and HTMX, leaving users with a broken experience if JavaScript is disabled. **Why:** Progressive enhancement ensures that your application is accessible to the widest possible audience, regardless of their browser capabilities or network conditions. **Example:** """html <!-- Form that works without JavaScript (basic submission) --> <form action="/submit-form" method="post" hx-post="/submit-form" hx-target="#form-result" hx-swap="innerHTML" hx-disable> <input type="text" name="name"> <button type="submit">Submit</button> </form> <div id="form-result"></div> """ When JavaScript is enabled, HTMX will intercept the form submission and perform an AJAX request. If JavaScript is disabled, the form will submit normally to "/submit-form". Because the initial form works without HTMX, the basic request will still be processed. ### 3.2. Polling and Real-Time Updates **Standard:** Implement polling or server-sent events (SSE) for real-time updates when necessary. **Do This:** Use "hx-get" with "hx-trigger="every:{interval}ms"" to poll the server for updates at a specified interval. For more efficient real-time updates, consider using server-sent events (SSE) and the "text/event-stream" content type. **Don't Do This:** Overuse polling, as it can put unnecessary load on the server. Use websockets if your performance needs outgrow other solutions and you're already heavily invested in server-side functionality. **Why:** Polling and SSE enable you to create dynamic UIs that reflect real-time data changes. **Example (Polling):** """html <!-- Poll the server every 5 seconds --> <div id="realtime-data" hx-get="/realtime-data" hx-trigger="every:5s" hx-swap="innerHTML"> Loading... </div> """ ### 3.3. Component Loading States **Standard:** Provide clear visual feedback to the user while components are loading. **Do This:** Use placeholder content or loading indicators to indicate that data is being fetched. Employ the "htmx:beforeRequest" and "htmx:afterRequest" events to show and hide loading indicators dynamically. Leverage "hx-indicator" to show an element while an HTMX request is in flight. **Don't Do This:** Leave the user with no feedback while a component is loading, as this can create a confusing and frustrating experience. **Why:** Loading states improve the user experience by providing visual confirmation that the application is working. **Example:** """html <div id="my-component" hx-get="/data" hx-trigger="click"> <span class="htmx-indicator"> Loading... </span> </div> """ """css .htmx-indicator{ display: none; } .htmx-request .htmx-indicator{ display: inline; } """ ### 3.4. Custom Events and Triggers **Standard:** Use custom events and triggers to create more complex and dynamic component interactions. **Do This:** Define custom events in your JavaScript code and trigger them using "htmx:trigger". Listen for these events using "hx-trigger="my-custom-event"". This can allow for more intricate component communication without resorting to complicated selectors. **Don't Do This:** Overuse JavaScript for simple interactions that can be handled with standard HTMX attributes. Forget to namespace Javascript events to avoid possible conflicts. **Why:** Custom events provide a flexible mechanism for triggering HTMX requests and updating the UI based on complex application logic. **Example:** """html <!-- Trigger a request when a custom event is fired --> <div id="component-b" hx-get="/update-content" hx-trigger="component-a-updated" hx-swap="innerHTML"> Loading... </div> <button id="component-a">Update Component B</button> <script> document.getElementById('component-a').addEventListener('click', function() { htmx.trigger(document.getElementById('component-b'), 'component-a-updated'); // HTMX helper function }); </script> """ By adhering to these component design standards, you can build robust, maintainable, and user-friendly HTMX applications. This is your foundation for high-quality professional HTMX development.
# State Management Standards for HTMX This document outlines coding standards for managing state in HTMX applications. It emphasizes maintainability, performance, and security. It focuses on approaches to managing application state, data flow, reactivity, and how these principles apply to HTMX specifically. ## 1. Understanding State in HTMX Applications ### 1.1 What is State? State refers to the data an application uses to operate. In web applications, this can range from simple UI elements (like a button's disabled status) to complex server-side data. In HTMX, state management revolves around how data is fetched, displayed, updated, and persisted across HTTP requests and responses. ### 1.2 Challenges of State Management with HTMX HTMX, by design, pushes complexity to the server-side by leveraging the backend to handle significant parts of application logic. This means effective state management often involves: * Seamlessly synchronizing client-side UI with server-side data changes. * Minimizing unnecessary server round trips to improve performance. * Handling complex data relationships and dependencies efficiently. * Implementing robust error handling and data validation. ## 2. Core Principles for State Management ### 2.1 Server-Driven State * **Do This:** Centralize the source of truth for application state on the server. Use HTMX to request and render only the necessary UI fragments based on the server's state. * **Don't Do This:** Avoid excessive client-side state, especially for critical data. Relying heavily on client-side JavaScript to manage core application state negates the benefits of HTMX. * **Why:** Ensures data consistency, simplifies updates, and improves security by keeping critical logic on the server. ### 2.2 Minimal Client-Side State * **Do This:** Use client-side state sparingly, primarily for transient UI concerns like local form validation or temporary UI effects. HTMX's ability to update specific UI elements via AJAX requests reduces the need for elaborate client-side state management. * **Don't Do This:** Store persistent data like user IDs or session tokens in client-side JavaScript variables or local storage without proper security measures. * **Why:** Reduces complexity, minimizes the risk of client-side vulnerabilities, and leverages the server for data integrity. ### 2.3 Idempotent Requests * **Do This:** Design HTMX endpoints to be idempotent where possible. This means that making the same request multiple times has the same effect as making it once. For example, a "toggle" button should be handled on the server in a way that consistently flips the state, regardless of how many times the request is sent. * **Don't Do This:** Implement endpoints that rely on request order for correct state updates, as network conditions can cause out-of-order requests. * **Why:** Improves resilience and simplifies error handling. If a request fails, it is safe to retry it without causing unintended side effects. ### 2.4 Optimistic Updates * **Do This:** For immediate UI feedback, consider optimistic updates – updating the UI as if the request will succeed, then reverting the UI if an error occurs. Use "hx-swap" modifiers like "settle" to manage UI transitions smoothly. * **Don't Do This:** Optimistically update the UI without implementing proper error handling and rollback mechanisms. * **Why:** Improves user experience by providing instant feedback, but requires careful error management to prevent data inconsistencies. ### 2.5 Data Contracts * **Do This:** Define clear data contracts between the client and server. Use shared schemas (e.g., JSON Schema, OpenAPI) to ensure both sides understand the structure of requests and responses. * **Don't Do This:** Make implicit assumptions about data formats or types. This can lead to unexpected errors and data corruption. * **Why:** Improves maintainability, reduces integration errors, and provides clear documentation for developers. ## 3. Implementing State Management Techniques ### 3.1 Server-Side Sessions * **Standard:** Use server-side sessions for managing user authentication, authorization, and persistent data. HTMX interacts with session data indirectly through HTTP requests and responses. * **Do This:** Store user-specific data in session variables or cookies. Authenticate users and authorize based on server-side session data. * **Don't Do This:** Expose sensitive session data directly to the client. """html <!-- Example: Displaying user-specific data based on session data --> <div id="user-info" hx-get="/user/profile" hx-trigger="load"> <!-- Content will be loaded from /user/profile --> </div> """ """python # Example (Python/Flask): Server endpoint for user profile from flask import Flask, session, render_template app = Flask(__name__) app.secret_key = 'your_secret_key' # Change in production! @app.route("/user/profile") def user_profile(): if 'user_id' in session: user_id = session['user_id'] # Fetch user data from database based on user_id user_data = get_user_data(user_id) # Assume get_user_data is a function to fetch from DB return render_template('user_profile.html', user=user_data) else: return "<p>Please log in.</p>" """ ### 3.2 "hx-vals" for Passing Client-Side Data * **Standard:** Use "hx-vals" to pass context directly from the client to the server alongside requests. This is helpful for including simple form data or UI state (avoid complex client objects). * **Do This:** Use JSON syntax to encode complex data structures in "hx-vals". Keep the data minimal and specific to the current request. * **Don't Do This:** Include large or sensitive data in "hx-vals". Always validate data received from the client on the server. """html <!-- Example: Passing form data with hx-vals --> <form hx-post="/submit" hx-target="#result"> <input type="text" name="username" id="username" value="default_user"> <button hx-vals='{"theme": "dark", "priority": "high"}'>Submit</button> </form> <div id="result"></div> """ """python # Example (Python/Flask): Server endpoint handling hx-vals data from flask import Flask, request app = Flask(__name__) @app.route("/submit", methods=['POST']) def submit_form(): username = request.form.get('username') theme = request.values.get('theme') # access data sent in hx-vals priority = request.values.get('priority') # Process the form data and theme/priority result_message = f"Username: {username}, Theme: {theme}, Priority: {priority}" return f"<p>{result_message}</p>" """ ### 3.3 Hidden Inputs for Carrying State * **Standard:** Use hidden input fields to maintain state across multiple HTMX requests, especially when dealing with multi-step forms or complex UI flows. * **Do This:** Update hidden input fields using HTMX's "hx-swap="outerHTML"" or "hx-swap="innerHTML"" to ensure they contain the latest state. * **Don't Do This:** Rely solely on URL parameters for state, as they can become unwieldy and difficult to manage. """html <!-- Example of using hidden input to maintain state across requests --> <div id="myForm"> <p>Step 1: Enter your name</p> <input type="text" name="name" hx-post="/step2" hx-target="#myForm" hx-swap="outerHTML"> <input type="hidden" id="currentStep" name="currentStep" value="1"> </div> """ """python # Python/Flask example server side: from flask import Flask, request, render_template app = Flask(__name__) @app.route("/step2", methods=['POST']) def step2(): name = request.form['name'] current_step = int(request.form['currentStep']) next_step = current_step + 1 return render_template('step2.html', name=name, current_step=next_step) """ """html <!-- step2.html --> <div id="myForm"> <p>Step 2: Confirm your name</p> <p>Name: {{ name }}</p> <input type="hidden" id="currentStep" name="currentStep" value="{{ current_step }}"> <button hx-post="/submitForm" hx-target="#result" hx-swap="outerHTML">Submit</button> </div> <div id="result"></div> """ ### 3.4 URL-Based State * **Standard:** Use URL parameters or the fragment identifier (hash) to represent shareable or bookmarkable application state. * **Do This:** Use "hx-push-url" to update the browser's URL when the application state changes. Consider using a utility library to parse and serialize URL parameters. * **Don't Do This:** Store sensitive data in URL parameters as they are visible in browser history and can be easily shared. """html <!-- Example: Updating the URL with hx-push-url --> <button hx-get="/items?page=2" hx-target="#items" hx-push-url="true">Next Page</button> <div id="items"> <!-- Items will be loaded here --> </div> """ ### 3.5 Client-Side Data Storage (Use Sparingly) * **Standard:** Use browser-based storage (localStorage or sessionStorage) only for non-sensitive, short-lived data. Use it judiciously as it adds complexity to the client-side. * **Do This:** Encrypt sensitive data before storing it in localStorage. Always validate data retrieved from localStorage before using it. Limit the amount of data stored in localStorage to avoid performance issues. * **Don't Do This:** Store sensitive information like passwords or API keys directly in localStorage. *Important: Storing sensitive data, even encrypted, on the client-side comes with inherent risks. Avoid it if possible.* """javascript // Example (JavaScript): Using localStorage to store theme preference function setTheme(theme) { localStorage.setItem('theme', theme); document.documentElement.className = theme; } function getTheme() { return localStorage.getItem('theme') || 'light'; // Default to light } // On page load: document.addEventListener('DOMContentLoaded', () => { setTheme(getTheme()); }); """ """html <!-- HTML implementation to toggle themes --> <button onclick="toggleTheme()">Toggle Theme</button> <script> function toggleTheme() { const currentTheme = localStorage.getItem('theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; setTheme(newTheme); // Using the setTheme function from the previous example } </script> """ ### 3.6 Websockets for Real-Time Updates * **Standard:** Use WebSockets for real-time updates and bidirectional data flow. Integrate with HTMX to dynamically update UI elements based on WebSocket messages. * **Do This:** Use a well-established WebSocket library (e.g., Socket.IO, SockJS). Ensure WebSocket connections are properly authenticated and authorized. * **Don't Do This:** Abuse WebSockets for simple, infrequent updates that can be handled with regular HTTP requests. """html <!-- Example of using WebSockets with HTMX --> <div id="realtime-data"> <!-- Data will be updated via WebSocket --> Loading... </div> <script> // Example (JavaScript): Connecting to a WebSocket and updating the UI const socket = new WebSocket('ws://localhost:8000/ws'); socket.onmessage = function(event) { const data = JSON.parse(event.data); document.getElementById('realtime-data').innerHTML = data.message; }; </script> """ ### 3. 7 "hx-ext": WebSockets Extension HTMX provides a websocket extension - "hx-ext="ws"". This extension simplifies websocket connection and updates and allows you to use HTMX attributes to control the websocket connection and UI updates. * **Standard:** Use the "ws" extension is often preferable to manually managing websocket connections in javascript as in the previous example * **Do This:** Use attributes like "ws-connect", "ws-send", and "ws-swap". * **Don't Do This:** Mix manual Javascript websocket connection handling with the "ws" extension for the same element. """html <!-- Connect to websocket --> <div id="myDiv" ws-connect="/ws"> Websocket Not Connected. </div> <!-- After connection, the DIV will automatically be populated by the server --> <!-- Send data to the server --> <input type="text" ws-send="keyup" /> """ ## 4. Common Anti-Patterns ### 4.1 Over-reliance on Client-Side JavaScript * **Anti-Pattern:** Using JavaScript to manage core application state that should be handled on the server. * **Solution:** Refactor the application to move state management logic to the server. Use HTMX to request and render only the necessary UI fragments. ### 4.2 Inconsistent Data Validation * **Anti-Pattern:** Performing data validation only on the client-side, without server-side validation. * **Solution:** Always validate data on both the client and server. Client-side validation provides immediate feedback, while server-side validation ensures data integrity. ### 4.3 Mixing Concerns * **Anti-Pattern:** Combining data fetching, state management, and UI rendering logic in a single component. * **Solution:** Separate concerns by using dedicated components or services for each task. This improves testability and maintainability. ### 4.4 Neglecting Error Handling * **Anti-Pattern:** Neglecting to handle errors gracefully, especially when performing optimistic updates. * **Solution:** Implement comprehensive error handling mechanisms to ensure that the UI is updated correctly, even when requests fail. ## 5. Security Considerations ### 5.1 Input Sanitization * **Standard:** Sanitize all user inputs on the server to prevent XSS attacks. Use a well-established sanitization library or framework. * **Do This:** Use parameterized queries to prevent SQL injection attacks. * **Don't Do This:** Trust client-side data without proper validation and sanitization. ### 5.2 Authentication and Authorization * **Standard:** Implement robust authentication and authorization mechanisms to protect sensitive data and functionality. * **Do This:** Use server-side sessions to manage user authentication state. * **Don't Do This:** Expose sensitive data or functionality to unauthorized users. ### 5.3 CSRF Protection * **Standard:** Implement CSRF protection to prevent cross-site request forgery attacks. * **Do This:** Include a CSRF token in forms and AJAX requests. Validate the token on the server. ## 6. Performance Optimization ### 6.1 Minimize Network Requests * **Standard:** Reduce the number of HTTP requests by batching updates or using data compression techniques. * **Do This:** Use caching to store frequently accessed data on the server or client. * **Don't Do This:** Make unnecessary requests for data that is already available. ### 6.2 Optimize Rendering * **Standard:** Optimize UI rendering by using efficient DOM manipulation techniques. * **Do This:** Use HTMX's "hx-swap" modifiers to control how UI elements are updated. * **Don't Do This:** Re-render entire pages when only small updates are necessary. ### 6.3 Use Caching Strategically * **Standard:** Implement caching effectively at various levels—browser, CDN, server—to minimize latency and reduce server load. * **Do This:** Leverage HTTP caching headers (e.g., "Cache-Control", "ETag") to instruct browsers and CDNs on how to cache responses. Use server-side caching mechanisms (e.g., Redis, Memcached) for frequently accessed data. * **Don't Do This:** Cache sensitive or personalized data without proper security measures. Set excessively long cache durations, which can lead to stale data. * **Example:** Setting Cache-Control headers in Python (Flask) """python from flask import Flask, make_response app = Flask(__name__) @app.route('/cached_data') def cached_data(): data = fetch_expensive_data() # Assume this function fetches data that takes time response = make_response(data) response.headers['Cache-Control'] = 'public, max-age=3600' # Cache for 1 hour return response """ ## 7. Testing State Management ### 7.1 Unit Tests * **Standard:** Write unit tests to verify the behavior of individual components and functions involved in state management. ### 7.2 Integration Tests * **Standard:** Write integration tests to verify the interaction between different components and services. ### 7.3 End-to-End Tests * **Standard:** Write end-to-end tests to verify the overall behavior of the application, including state management. ## 8. Conclusion These coding standards provide a foundation for building maintainable, performant, and secure HTMX applications. By following these guidelines, developers can effectively manage state and leverage the full potential of HTMX while avoiding common pitfalls. Remember to adapt these standards to the specific needs of your project and stay up-to-date with the latest HTMX features and best practices.
# Performance Optimization Standards for HTMX This document outlines coding standards and best practices to optimize the performance of HTMX applications, ensuring speed, responsiveness, and efficient resource utilization. These standards are guided by the latest HTMX documentation, community best practices, and modern web development patterns. ## 1. Server-Side Rendering and Caching ### 1.1. Standard: Optimize Server-Side Logic **Do This:** * Implement efficient server-side rendering logic. * Use caching mechanisms to reduce database queries and computation time. * Prefer optimized database queries. * Profile server-side code to identify bottlenecks. **Don't Do This:** * Perform complex calculations or data transformations on each HTMX request needlessly. * Overload the server with unnecessary processing. * Ignore database query optimization (e.g., missing indexes). **Why:** Server-side performance directly impacts how quickly HTMX can render and send HTML fragments to the client. Inefficient server-side code leads to longer response times, degrading the user experience. **Code Example (Python/Flask with caching):** """python from flask import Flask, render_template, request, cache from datetime import datetime app = Flask(__name__) cache.init_app(app, config={'CACHE_TYPE': 'SimpleCache'}) # Use a simple cache @app.route('/time') @cache.cached(timeout=60) # Cache the response for 60 seconds def get_time(): now = datetime.now().strftime("%H:%M:%S") print("Recomputing time") # for debugging return render_template('time.html', time=now) if __name__ == '__main__': app.run(debug=True) """ """html <!-- time.html --> <div id="server-time"> Server Time: <span hx-swap="innerHTML transition:true" hx-get="/time" hx-trigger="every 5s">{{ time }}</span> </div> """ **Anti-Pattern:** Performing the same database query on every HTMX request without leveraging caching. **Technology-Specific Detail:** Proper cache invalidation strategies are crucial. Using very long cache times without a mechanism to force updates will serve users very stale data. ### 1.2. Standard: Efficient HTML Fragments **Do This:** * Keep HTML fragments small and focused, containing only the necessary elements to update the target area. * Optimize the size of HTML by removing unnecessary whitespace and comments (minification). **Don't Do This:** * Return entire page layouts or large sections of the DOM when only a small update is needed. * Include large images or datasets directly in the HTML fragments, defer them to requests afterward. **Why:** Smaller HTML fragments translate to faster transfer times and quicker DOM updates. Sending large amounts of unnecessary HTML bloats network traffic and slows down the browser. **Code Example (Optimized HTML Fragment):** """html <!-- Instead of this: --> <div id="user-profile"> <h1>John Doe</h1> <p>Email: john.doe@example.com</p> <p>Location: New York</p> <!-- many more fields --> </div> <!-- Send only the updated email: --> <p>Email: jane.doe@example.com</p> """ """html <!-- Target Element --> <div id="user-email"> <p>Email: john.doe@example.com</p> </div> """ """html <!-- HTMX request updates user email --> <button hx-get="/update-email" hx-target="#user-email" hx-swap="outerHTML">Update Email</button> """ **Anti-Pattern:** Sending the entire "<div id="user-profile">" block whenever the email changes, even though only a single sub-element's text content is dynamic. **Technology-Specific Detail:** Use tools like HTML minifiers (e.g., "htmlmin" in Python) to reduce the size of rendered HTML before sending it to the client. ## 2. Client-Side Optimization ### 2.1. Standard: Minimize Network Requests **Do This:** * Combine multiple HTMX requests into a single request where possible using request parameters or request headers. * Leverage browser caching for static assets (images, CSS, JavaScript). * Consider using [Out-of-Band Updates](https://htmx.org/attributes/hx-swap/) to update multiple parts of the DOM with a single request. * Use "hx-include" judiciously to send only *required* additional data in the request. **Don't Do This:** * Make excessive, chatty requests for small pieces of data. * Fail to leverage browser or CDN caching. * Include unnecessary data within the HTMX request. **Why:** Each HTTP request introduces overhead due to network latency, TLS handshake, and server processing. Reducing the number of requests improves perceived and actual performance. **Code Example (Combining Requests):** """html <!-- Instead of separate requests for name and location --> <button hx-get="/update-name" hx-target="#user-name">Update Name</button> <button hx-get="/update-location" hx-target="#user-location">Update Location</button> <!-- Combine into a single request --> <button hx-get="/update-user?field=name & field=location" hx-target="#user-name, #user-location">Update Both</button> """ """python # Server-side (Python/Flask): from flask import Flask, request, render_template app = Flask(__name__) @app.route('/update-user') def update_user(): fields = request.args.getlist('field') # Get a list of fields to update # Logic here to update the user based on the 'fields' requested if "name" in fields: name = "Jane Doe" # updated name else: name = "John Doe" if "location" in fields: location = "Los Angeles" # updated location else: location = "New York" return render_template("user_details.html",name=name, location=location) # Returning multiple updates in one request # user_details.html # Using hx-swap="outerHTML" allows independent targets, separated by commas in hx-target # Note that this template must contain *both* the user-name AND user-location DIVs. This approach sacrifices # isolation of concerns at the server in order to gain a performance win. ''' <div id="user-name"> <h3>{{name}}</h3> </div> <div id = "user-location"> <h4>{{location}}</h4> </div> ''' if __name__ == '__main__': app.run(debug=True) """ **Anti-Pattern:** A button that triggers five separate HTMX requests to update five different elements on the page, when all data is already available on the server. **Technology-Specific Detail:** Use HTTP/2's multiplexing feature by ensuring your server and browser support it. This allows multiple requests to be sent over a single TCP connection, reducing overhead. ### 2.2. Standard: Strategic Use of "hx-swap" **Do This:** * Choose the most efficient "hx-swap" strategy for each scenario. Use "innerHTML" when updating only the content of an element, and "outerHTML" when replacing the entire element. * Understand the implications of using "hx-swap="none"" with "hx-oob" to perform out-of-band updates, thus minimizing content transfer for your primary request. * Consider the "transition:true" modifier for smoother updates. **Don't Do This:** * Always use "outerHTML" when "innerHTML" would suffice. * Overuse "hx-swap="beforebegin"" or "hx-swap="afterend"" which can cause unexpected DOM manipulation issues, particularly element duplication or detachment of javascript event handlers. * Ignore the performance implications of "hx-swap", especially when dealing with large or complex DOM structures. **Why:** Different "hx-swap" strategies have different performance characteristics. "innerHTML" is typically faster than "outerHTML" because it avoids re-rendering the container element. "hx-swap="none"" can completely bypass rendering client content entirely. **Code Example:** """html <!-- Better (innerHTML): only content changes --> <div id="message"> {{ message }} </div> <button hx-get="/get-message" hx-target="#message" hx-swap="innerHTML">Update Message</button> <!-- Worse (outerHTML): entire div is replaced when only the message changes (larger transfer size and more DOM manipulation) --> <div id="message"> {{ message }} </div> <button hx-get="/get-message" hx-target="#message" hx-swap="outerHTML">Update Message</button> """ """html <!-- Using Out-of-Band to display a simple notification --> <div id="main-content"> Main content here. <!-- Action to trigger --> <button hx-post="/submit-form" hx-target="#main-content">Submit</button> </div> <!-- Response (from /submit-form): --> <div id="main-content"> Main content after submission. </div> <div id="notification" class="notification" hx-swap-oob="afterbegin:#main-content">Form submitted successfully!</div> """ **Anti-Pattern:** Using "outerHTML" when only the text content of a "<span>" needs to be updated. **Technology-Specific Detail:** Be aware of the browser's rendering pipeline. Excessive DOM manipulations can trigger re-layouts and re-paints, impacting performance. The "transition:true" modifier on "hx-swap" uses browser-native transitions for a smoother user experience. ### 2.3. Standard: Caching HTMX Responses (if possible) **Do This:** * Implement client-side caching of HTMX responses for idempotent "GET" requests where the data doesn't change frequently. Use techniques like "localStorage" or "sessionStorage" to store the HTML fragments. Remember that caching POST/PUT/DELETE requests often violates the HTTP spec. * Consider using "hx-ext="cache"" (community extension) for caching. **Don't Do This:** * Cache dynamic or personalized content without appropriate cache invalidation. * Cache sensitive data insecurely in "localStorage". * Forget to implement cache busting strategies (e.g., versioned URLs) when updating cached content. **Why:** Caching reduces the number of server requests and speeds up subsequent interactions. **Code Example (Caching with localStorage):** """html <div id="cached-content"> Loading... </div> <script> document.addEventListener('DOMContentLoaded', function() { const cachedContent = localStorage.getItem('myCachedData'); if (cachedContent) { document.getElementById('cached-content').innerHTML = cachedContent; } else { //Load and cache it htmx.ajax('GET', '/get-cached-data', '#cached-content') .then(function(xhr) { localStorage.setItem('myCachedData', xhr.responseText); }); } }); htmx.on('htmx:afterSwap', function(evt) { if (evt.target.id === 'cached-content') { localStorage.setItem('myCachedData',document.getElementById('cached-content').innerHTML); } }); // Add a button to clear cache (for testing) function clearCache() { localStorage.removeItem('myCachedData'); document.getElementById('cached-content').innerHTML = 'Loading...'; location.reload(); // Force reload to re-fetch } </script> <button onclick="clearCache()">Clear Cache</button> """ """python # Server-side (Python/Flask): from flask import Flask, render_template app = Flask(__name__) @app.route('/get-cached-data') def get_cached_data(): # Generate or retrieve the data that needs to be cached data = "This is the cached data from the server." return data # Return it directly as plain text. Flask will set Content-Type to text/html """ **Anti-Pattern:** Caching user-specific data in "localStorage" without encryption or proper access controls. **Technology-Specific Detail:** Use "sessionStorage" for data that should be cached only for the current browser session, and "localStorage" for data that should persist across sessions. Implement proper cache invalidation to ensure users always see the latest content. ## 3. Utilizing HTMX Extensions and Attributes Efficiently ### 3.1. Standard: Load Extensions Strategically **Do This:** * Only load the HTMX extensions that are required for the specific page or component. * Load extensions asynchronously to avoid blocking the main thread. * Consider a bundler to reduce the overall size of extensions/HTMX. **Don't Do This:** * Load all HTMX extensions on every page, regardless of whether they are used. * Load extensions synchronously in the "<head>" of the document, blocking rendering. **Why:** Loading unnecessary extensions increases the JavaScript payload, slowing down page load times. Synchronous loading blocks the main thread, preventing the browser from rendering the page. **Code Example:** """html <!-- Load only the necessary extensions --> <script src="https://unpkg.com/htmx.org@1.9.10"></script> <script src="https://unpkg.com/htmx.org/dist/ext/ws.js"></script> <!-- Only needed on pages that use WebSockets --> <script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script> <!-- only needed when using application/json --> """ **Anti-Pattern:** Including "<script src="https://unpkg.com/htmx.org/dist/ext/debug.js"></script>" in a production environment. This extension is useful for debugging but adds unnecessary overhead. **Technology-Specific Detail:** Most CDNs support asynchronous loading of JavaScript files. Use the "async" or "defer" attribute in the "<script>" tag. ### 3.2. Standard: Optimizing "hx-vals" for form submissions and requests **Do This:** * Use "hx-vals" only to include parameters that are *not* already within a "<form>", or easily obtained via "hx-include". Otherwise, allow HTMX to naturally serialize the form for you. * Ensure that values passed via "hx-vals" have the correct type according to the server's API specification. * Avoid complex javascript or templating expressions within "hx-vals". These are generally unreadable and difficult to maintain. **Don't Do This:** * Duplicate information already present in a "<form>". * Generate or compute complex values directly in the attribute string if it can be avoided. Rely on server-side computation and caching. **Why:** Proper use of "hx-vals" ensures that only the minimum required data is sent with each request. Overly-complex "hx-vals" reduces code maintainability. **Code Example:** """html <!-- Pass a dynamic value from javascript, only when necessary --> <button hx-post="/update-date" hx-vals='js:{currentDate: new Date().toISOString()}' hx-target="#date-display">Update Date</button> <div id="date-display"></div> """ """python # Flask Example from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/update-date', methods=['POST']) def update_date(): current_date = request.json.get('currentDate') # Extract the date return jsonify({"message": f"Date updated to {current_date}"}) if __name__ == '__main__': app.run(debug=True) """ **Anti-Pattern:** Generating complex JSON objects directly within the "hx-vals" attribute, leading to unreadable and unmaintainable code or duplicating Form data that's already accessible. **Technology-Specific Detail:** Consider implementing custom JavaScript functions to handle complex data transformations, and then reference these functions from "hx-vals" for improved code organization. ## 4. Monitoring and Profiling ### 4.1. Standard: Performance Monitoring Tools **Do This:** * Utilize browser developer tools (Network tab, Performance tab) to analyze HTMX request timings and identify bottlenecks. * Employ server-side monitoring tools to track request latency, database query times, and CPU usage. * Use tools such as Lighthouse or WebPageTest to assess overall website performance. **Don't Do This:** * Rely solely on anecdotal evidence to assess performance. * Ignore or dismiss performance monitoring data. * Forget to use profiling tools to analyze server-side bottlenecks or slow database queries. **Why:** Monitoring and profiling provide essential data for identifying and addressing performance issues. Metrics help quantify improvements after optimization efforts. **Code Example:** (N/A - Conceptual. This standard involves the *use* of external tools for assessment.) **Anti-Pattern:** Deploying major HTMX changes to production without conducting any performance testing or analysis. **Technology-Specific Detail:** HTMX emits custom events that can be captured and analyzed using JavaScript. Use these events to track HTMX-specific performance metrics, such as request start times, response times, and swap durations. Be sure to disable these when the site is live, however. ### 4.2. Standard: Continuous Performance Improvement **Do This:** * Establish a culture of continuous performance improvement within the development team. * Regularly review performance metrics and identify areas for optimization. * Prioritize performance improvements alongside feature development and bug fixes. **Don't Do This:** * Treat performance optimization as an afterthought. * Ignore performance regressions introduced by new code changes. * Never re-assess the performance of critical workflows as the underlying data/infrastructure changes. **Why:** Continuous performance improvement ensures that the application remains fast and responsive as it evolves. Performance should be a core consideration throughout the development lifecycle. **Code Example:** (N/A - Cultural/Organizational. This standard is about process and mindset.) **Anti-Pattern:** Delaying performance optimization until the application is in production and experiencing performance problems. **Technology-Specific Detail:** Integrate performance testing into the CI/CD pipeline to automatically detect performance regressions. Create automated tests that simulate user interactions and measure response times. By following these performance optimization standards, development teams can build HTMX applications that are fast, responsive, and efficient, providing a superior user experience and reducing infrastructure costs.