Tracking document for migrating NyasaBlog from a traditional server-rendered Django app to a reactive HTMX-powered experience.
Base branch: master
Started: 2026-04-08
| Phase | Branch | Description | Status |
|---|---|---|---|
| 0 | htmx/phase-0-foundation |
Foundation + Toast + Auth Guard + CSRF | Done |
| 1 | htmx/phase-1-likes-bookmarks |
Likes & Bookmarks (validates setup) | Done |
| 2 | htmx/phase-2-newsletter |
Newsletter Subscribe | Done |
| 3 | htmx/phase-3-comments |
Comments (submit, delete, OOB count) | Done |
| 4 | htmx/phase-4-pagination |
Pagination / Infinite Scroll | Done |
| 5 | htmx/phase-5-category-filter |
Category Filtering (explicit ?partial=) | Done |
| 6 | htmx/phase-6-live-search |
Live Search (header dropdown + page) | Done |
| 7 | htmx/phase-7-forms |
Form Submissions (login, register, contact, account) | Done |
| 8 | htmx/phase-8-cleanup |
Cleanup (remove dead JS, add indicators) | Done |
- HX-Trigger for toasts — not OOB HTML appended to every response. Single global JS listener.
@htmx_login_requireddecorator — returns 401 +HX-Reswap: none+ toast for unauthenticated HTMX requests. Prevents login page HTML from being swapped into a button target.- Explicit
?partial=query param — for differentiating partial responses (e.g., pagination vs. category filtering). More resilient thanrequest.htmx.target. - Progressive enhancement — partials are
{% include %}d in parent templates, so the site works without JS. - JSON API preserved — views branch on
request.htmx; DRF API and future mobile clients unaffected. - CKEditor forms stay traditional — CKEditor DOM state doesn't survive HTMX swaps.
- No
hx-boost— too much risk for admin, CKEditor, third-party pages. Per-element HTMX is sufficient.
requirements.txt— adddjango-htmxmysite/settings.py— adddjango_htmxtoINSTALLED_APPS, addHtmxMiddlewaretemplates/base.html— add HTMX script, CSRF config, toast container, global error handlertemplates/snippets/base_css.html— add htmx-indicator + toast animation CSSblog/utils.py—trigger_toast()helper,@htmx_login_requireddecorator- Create
partials/directories
- HTMX loads on every page (check Network tab)
-
request.htmxis available in views (add a print statement) - CSRF token is sent with HTMX POST requests
- Toast fires from browser console:
document.body.dispatchEvent(new CustomEvent('showToast', {detail: {message: 'Test', type: 'success'}}))
blog/templates/blog/partials/like_button.html— new partialblog/templates/blog/partials/bookmark_button.html— new partialblog/views.py— modifytoggle_like_view,toggle_bookmark_viewto return partials for HTMXblog/templates/blog/detail_blog.html— replace inline buttons with{% include %}, deletetoggleLike/toggleBookmarkJSblog/templates/blog/bookmarks.html— replace inlinefetch()withhx-post
- Like toggles without page reload, count updates, toast shows
- Bookmark toggles without page reload, toast shows
- Bookmark removal on bookmarks page removes the card with transition
- Unauthenticated user sees toast "Please log in" instead of broken swap
- JSON API still works (
curl -X POST)
personal/views.py— modifysubscribe_viewfor HTMX- Multiple templates — replace
submitNewsletter()withhx-postforms templates/base.html— deletesubmitNewsletterfunction
- Subscribe works without page reload
- Duplicate email shows "already subscribed" message
- Empty email shows error
blog/templates/blog/partials/comment_item.html— newblog/templates/blog/partials/comment_form.html— newblog/templates/blog/partials/reply_item.html— newblog/urls.py— addcomment_createURLblog/views.py— addhtmx_create_comment_view, modifydelete_comment_viewblog/templates/blog/detail_blog.html— HTMX comment form + list
- Comment submits without reload, appears at top of list, form resets
- Reply submits without reload, appears under parent
- Comment delete removes element, shows confirm dialog
- Comment count updates via OOB swap
- Unauthenticated user gets toast, not broken swap
personal/templates/personal/partials/post_grid.html— newpersonal/views.py— modifyhome_screen_viewpersonal/templates/personal/home.html— use{% include %}, remove pagination block
- Scrolling to bottom loads more posts
- Last page doesn't show loading sentinel
- Initial page load still works without JS
personal/templates/personal/partials/home_content.html— newpersonal/templates/personal/home.html— HTMX category pills withhx-valspersonal/views.py— differentiate via?partial=param
- Clicking a category filters posts without reload
- URL updates via
hx-push-url - Back button works correctly
- Direct URL access still renders full page
personal/templates/personal/partials/search_dropdown.html— newtemplates/snippets/header.html— HTMX search inputpersonal/views.py— modifysearch_viewfor dropdown partial
- Typing in header search shows dropdown after 300ms
- Results update as you type
- Clicking a result navigates to the post
- "View all results" links to full search page
account/templates/account/partials/login_form.html— newaccount/templates/account/partials/register_form.html— newpersonal/templates/personal/partials/contact_success.html— newaccount/views.py— modify login, register, account viewspersonal/views.py— modify contact view- Template files — add
hx-postattributes
- Login errors show inline without reload
- Successful login redirects via
HX-Redirect - Registration errors show inline
- Contact form shows success message without reload
- Account settings saves with file upload
- Remove all dead inline JS
- Add
hx-indicatorspinners to all HTMX forms - Final review of all templates for missed opportunities
- No inline
fetch()calls remain (except theme toggle) - Loading indicators visible during slow requests
- All pages functional with and without JS