Skip to content

feat: comprehensive blog example with module-based component layout#5

Merged
jackielii merged 4 commits into
mainfrom
feat/blog-modular-example
May 7, 2026
Merged

feat: comprehensive blog example with module-based component layout#5
jackielii merged 4 commits into
mainfrom
feat/blog-modular-example

Conversation

@jackielii
Copy link
Copy Markdown
Owner

Summary

Adds examples/blog/ — a full blog (public reader + admin CMS) organized as per-feature Go packages with shared ui/ packages, mirroring the layout you'd reach for in a typical React/Next app. Replaces the never-merged single-package attempt in PR #2; please close that one when this lands.

Why a new example

None of the existing examples use WithArgs DI, page-level Middlewares(), a custom error handler, deep nested routes, or cross-package component composition. This one does — and demonstrates them in a coherent app rather than a contrived snippet.

What it shows

Pattern Where
Per-feature Go packages (blog/, admin/) + shared ui/layout, ui/components tree
Cross-package component composition with one-way import direction every .templ
Ref("loginPage") for cross-feature links to avoid import cycles ui/layout/layout.templ
Nested route hierarchies, 3 levels (/admin/posts/{id}/edit) admin/routes.go, admin/posts.go
WithArgs(store, authSvc) consumed by Props/ServeHTTP/Middlewares main.go + every Props
Page-level Middlewares() returning RequireAdmin (with DI) admin/routes.go
Props(r, target RenderTarget, *store.Store) with conditional widget loads admin/dashboard.templ, blog/search.templ
Standalone function components as HTMX targets via target.Is(StatsGrid) then RenderComponent admin/dashboard.templ, admin/components.templ
Page() + Content() split for HTMX #content swaps blog/home.templ, admin/dashboard.templ
ServeHTTP form handlers — redirect for non-HTMX, RenderComponent(...) partial for HTMX blog/comment.go, admin/posts.go
WithErrorHandler rendering a styled cross-package error component (full doc + HTMX block) main.go + ui/components
URLFor with path params + query-string templates []any{p, "?page={page}"} blog/category.templ
ID/IDTarget wiring for hx-target throughout

App features

  • Public reader: home, post detail, category archive with pagination, fuzzy search, comments
  • Admin CMS: cookie-session auth (admin/admin), dashboard with two HTMX-refreshed widgets (StatsGrid, RecentPostsCard), post + user CRUD
  • In-memory store seeded at boot; Tailwind via Play CDN — no asset pipeline

Notes for reviewers

  • Login placement is non-obvious: admin.LoginPage is mounted as a sibling of admin.Pages (not a child) because admin.Pages.Middlewares() returns RequireAdmin, which would otherwise gate the login form itself.
  • Admin POST routes use explicit verbs (/posts/create, /posts/{id}/update, /posts/{id}/delete) instead of POST /posts/{id} to avoid Go's ServeMux conflict with literal segments like /posts/new.
  • README explicitly lists what's omitted vs production (plaintext passwords, no CSRF, in-memory sessions, etc.).

Test plan

  • go vet ./... at repo root — clean
  • templ generate -include-version=false && go build ./... && go vet ./... in examples/blog/ — clean
  • go test ./... at repo root — framework tests still pass
  • Public reader: /, /posts/welcome, /categories/guides?page=1, /search?q=props all 200
  • Custom 404 on /posts/nope; HTMX HX-Request returns just ErrorBlock (301 bytes) vs full ErrorPage (~1KB)
  • Auth gating: unauthenticated /admin/ 303s to /admin/login; correct creds 303 to /admin/; logout invalidates session
  • HTMX widget refresh: HX-Target: stats-grid → 906 bytes (just widget); recent-posts-card → 1409 bytes; content → body fragment (3218 bytes); full doc → 4259 bytes
  • Comment form: HTMX POST returns refreshed CommentsList with new comment
  • Post CRUD: edit form prefilled, update redirects, HTMX delete refreshes PostsTable partial
  • Search: HTMX HX-Target: search-results returns just the results div

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.65%. Comparing base (67e5283) to head (646f896).

Additional details and impacted files
@@           Coverage Diff           @@
##             main       #5   +/-   ##
=======================================
  Coverage   96.65%   96.65%           
=======================================
  Files          13       13           
  Lines        1197     1197           
=======================================
  Hits         1157     1157           
  Misses         26       26           
  Partials       14       14           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

jackielii added 2 commits May 7, 2026 11:19
Per review on PR #5: ServeHTTP returns error, so handlers should propagate
store and parse errors to the framework's error pipeline rather than
discarding them with `_`. Touches every admin form action plus the auth
session-id generator that Login depends on.
URLFor and components.URL accept any for varargs, so int IDs can be passed
directly. templ also renders integer types in { ... } expressions and
attributes natively. Remove the redundant conversions and the now-unused
fmt/strconv imports they pulled in. Also surface strconv.Atoi error in
postEditPage.Props (was the last `id, _ :=` in a Props that returns error).
@jackielii jackielii merged commit 449e74d into main May 7, 2026
8 checks passed
@jackielii jackielii deleted the feat/blog-modular-example branch May 7, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant