Welcome to the HxComponents documentation! This guide will help you migrate from popular frontend frameworks or get started from scratch.
HxComponents is a Go-based component framework that uses HTMX for interactivity and templ for templating. It allows you to build interactive web applications with type-safe, server-side components while minimizing JavaScript.
Migrating from another framework?
- React Migration Guide - Coming from React
- Vue 3 Migration Guide - Coming from Vue 3
- Vue 2 Migration Guide - Coming from Vue 2
- Svelte Migration Guide - Coming from Svelte
Starting fresh?
- Getting Started - Setup and first component
Core Concepts:
- Getting Started - Project setup, registration, and workflow
- Component Lifecycle - Understanding BeforeEvent, AfterEvent, and event handlers
- State Management - How to handle component state
Building UIs:
- Component Composition - Parent-child relationships, slots, and patterns
- Advanced Patterns - HTMX techniques, animations, and optimizations
Quality Assurance:
- Testing - Unit, integration, and E2E testing strategies
- Common Gotchas - Troubleshooting and solutions
HxComponents follow a predictable lifecycle for each request:
Request → Parse Form Data → BeforeEvent → On{EventName} → AfterEvent → Process → Render → Response
BeforeEvent(ctx context.Context, eventName string) error
- Called before any event handler
- Use for loading data, authentication
- Return error to abort request
- Context provides request-scoped values and cancellation
On{EventName}() error
- Event handler (e.g.,
OnSubmit,OnAddItem) - Called when
hxc-eventparameter matches - Return error to indicate failure
AfterEvent(ctx context.Context, eventName string) error
- Called after successful event handler
- Use for saving data, side effects
- Return error to indicate failure
- Context provides request-scoped values and cancellation
Process(ctx context.Context) error
- Called after all events, before render
- Use for final transformations
- Return error to indicate failure
- Context provides request-scoped values and cancellation
Unlike client-side frameworks where state lives in memory, HxComponents are stateless - each request creates a new instance. You have several options for managing state:
Option 1: Hidden Form Fields
Best for: Simple state that doesn't need persistence
<input type="hidden" name="count" value={ fmt.Sprint(data.Count) } />Best for: User-specific state during a session
func (c *Component) BeforeEvent(ctx context.Context, eventName string) error {
// Load from session
c.Data = getFromSession("data")
return nil
}
func (c *Component) AfterEvent(ctx context.Context, eventName string) error {
// Save to session
saveToSession("data", c.Data)
return nil
}Best for: Persistent state that survives sessions
func (c *Component) BeforeEvent(ctx context.Context, eventName string) error {
// Load from database
c.Items = db.GetItems(ctx, c.UserID)
return nil
}
func (c *Component) AfterEvent(ctx context.Context, eventName string) error {
// Save to database
return db.SaveItems(ctx, c.UserID, c.Items)
}Components are Go structs that:
- Hold state in fields with
formtags - Implement event handlers as methods (
On{EventName}()) - Implement
Render()method - Optionally implement lifecycle hooks
type CounterComponent struct {
Count int `form:"count"`
}
func (c *CounterComponent) OnIncrement() error {
c.Count++
return nil
}
func (c *CounterComponent) Render(ctx context.Context, w io.Writer) error {
return Counter(*c).Render(ctx, w)
}Templates are written in templ syntax:
- Type-safe
- Compiled to Go code
- Support Go expressions and control flow
templ Counter(data CounterComponent) {
<div>
<button hx-post="/component/counter" hx-vals='{"count": { fmt.Sprint(data.Count) }, "hxc-event": "increment"}'>
+
</button>
<span>{ fmt.Sprint(data.Count) }</span>
</div>
}HTMX provides interactivity:
hx-post,hx-get- Make requestshx-target- Where to updatehx-swap- How to updatehx-vals- Send additional datahx-trigger- When to trigger
Advantages:
- ✅ Type safety at compile time
- ✅ Server-side rendering (better SEO)
- ✅ Simpler deployment (single binary)
- ✅ Less JavaScript (better performance)
- ✅ Direct database access
- ✅ No build step (for application code)
- ✅ Better security (logic stays on server)
Trade-offs:
- ❌ Less rich client-side interactions
- ❌ Network latency for every interaction
- ❌ No offline functionality
- ❌ Different mental model
Perfect for:
- Content-heavy applications
- Admin dashboards
- Internal tools
- Form-heavy applications
- SEO-critical pages
- Progressive enhancement
Not ideal for:
- Real-time collaborative editing
- Complex animations and transitions
- Offline-first applications
- Games or interactive graphics
- Applications requiring extensive client-side logic
All examples are in the /examples directory:
- Counter - Simple increment/decrement
- TodoList - Full CRUD with lifecycle hooks
- Search - Form submission and results
- Login - Authentication and redirects
- Profile - Complex forms with arrays
Run the examples:
cd examples
templ generate
go run main.goVisit http://localhost:8080
<form hx-post="/component/myform" hx-vals='{"hxc-event": "submit"}'>
<input name="email" type="email" />
<input name="password" type="password" />
<button type="submit">Submit</button>
</form><button hx-post="/component/action" hx-indicator="#spinner">
Submit
</button>
<span id="spinner" class="htmx-indicator">Loading...</span><input
type="checkbox"
hx-post="/component/toggle"
hx-swap="outerHTML swap:0.1s"
/><div hx-get="/component/data" hx-trigger="every 5s">
{ data.Value }
</div>- Create component struct with
formtags - Add event handlers as
On{EventName}()methods - Create templ template for rendering
- Add lifecycle hooks if needed
- Register component in main.go
- Run
templ generate - Test
# Watch for changes and regenerate
templ generate --watch
# In another terminal
go run main.go
# In another terminal (optional)
# Run tests
go test ./...- Keep components focused - Single responsibility
- Use lifecycle hooks - Load data in BeforeEvent, save in AfterEvent
- Test at multiple levels - Unit, integration, E2E
- Handle errors gracefully - Validate inputs, return meaningful errors
- Use
closestfor targeting - Avoid ambiguous selectors - Escape JSON properly - Use fmt.Sprintf with %q or json.Marshal
- Include form fields - Use hx-include when needed
- Name events carefully - Must match On{EventName} method
- Use sessions wisely - For temporary state
- Persist to database - For permanent state
- Documentation: You're reading it!
- Examples: Check
/examplesdirectory - GitHub Issues: Report bugs or request features
- Discussions: Ask questions and share ideas
Contributions are welcome! Please:
- Read the migration guides to understand the patterns
- Check existing issues and PRs
- Write tests for new features
- Follow the existing code style
- Update documentation
- Start with a migration guide or Getting Started
- Build your first component
- Read Advanced Patterns for HTMX techniques
- Study the examples in
/examples - Set up testing using Testing Guide
- Refer to Common Gotchas when stuck
Happy building with HxComponents! 🚀