My Experience Building a Social Website for My Startup with Go + HTMX 4 (Long Post)
▲ 169 r/htmx

My Experience Building a Social Website for My Startup with Go + HTMX 4 (Long Post)

https://preview.redd.it/kqel7842utjh1.jpg?width=2048&format=pjpg&auto=webp&s=b9cb2844e74e0c42bc80f89a5eba3eabd3a6a254

Conclusion first: HTMX is VERY production ready for a large-scaled project. And thankfully I ditched Vue.js 2/3 and Svelte 4 for HTMX. (Never used React though.)

I literally cried when HTMX mentioned they’re trying to be the next jQuery, that might not need to change for the next decade.

In the AI era, adding features becomes easy, but people just don’t know how to make things simple and “just work.” I really hope HTMX stays this way. (please u/_htmx 😭🙏)

The startup is basically an old Twitter clone, from when social media felt friendlier and the web didn’t feel like a bunch of boring mobile apps. Sorry for the mixed-language screenshots, I hadn’t finished internationalization yet.

 

Here are a few of my experiences with HTMX:

1. Make the website stupid and just make it work

We don’t use a cool dialog library like SweetAlert.js. The native alert(), confirm(), and prompt() just work. With hx-confirm, it’s even simpler.

If you need a form, just use <form>, and refresh the whole page if you can (or hx-post if you don’t want to), so you don’t have to worry about partial updates or syncing what changed.

 

2. We don’t use Alpine.js

It’s a very good solution if you want some hybrid JS, but it reintroduced the state management problem.

We had server-rendered JSON inside <script type="application/json"> so Alpine.js could load the data back. It worked, but caused some flickering since Alpine runs after the page is rendered, and introduced another state we had to manage.

so we decided to keep everything server-side rendered. (yea, fuck the network latency 🥳🎉)

We took this pretty far. When you upload photos in a post creation form, normally you’d just spawn the previews with JavaScript. Instead, we turn the images into blob URLs, pass those URLs to the server, and the server returns something like:

<div class="photo-preview">
    <img src="{{ .ImageURL }}">
</div>

So when the user edits the post later, we can render the exact same “photo preview” component on the server. One component, one rendering path.

>ℹ️ But there are also things you can just ditch.
>. 
>We had a post poll where you could use +/- buttons to add or remove option inputs with Alpine.js. Turns out we only support up to 4 options anyway, so we ditched the +/- JS and just put 4 fixed text inputs in the poll form.
>. 
>Now we can render the option text directly into the inputs, instead of outputting it as JSON in a <script> for Alpine.js to load back into them when user is editing the poll.

It sounds stupid. That’s kind of the point. You sacrifice some UX (user experience), but get much better DX (developer experience).

 

3. We don’t use Templ or framework with HTMX with Go

Since Templ requires build-tools, learning special syntax, IDE extensions, we decided to just use Go’s html/template and split the HTML into layouts, pages, and partials. The definitions look roughly like this:

type AppLayout struct {
    Navbar *Navbar
}

type ProfilePage struct {
    *AppLayout
    User         *User
    FollowButton *FollowButton
}

type FollowButton struct {
    IsFollowing bool
}

Then compose the data in the page handler:

user, err := db.GetUser(c.Param("username"))
if err != nil {
    return c.Abort(http.StatusInternalServerError)
}

layout := &AppLayout{
    Navbar: NewNavbar(),
}

// Tada- now you have a full page! 🎉
page := &ProfilePage{
    AppLayout:    layout,
    User:         NewUser(user),
    FollowButton: NewFollowButton(user.IsFollowing),
}

And if you want to render the Follow Button component:

{{ template "follow_button" .FollowButton }}

Since AI can write the boilerplate now, code can be stupid and boring, but easy to debug. And with HTMX, you can just return any partial when needed.

There’s no "runtime magic" to trace if error occurred, back when Vue.js + hot reload pointing stack traces to some generated chunks.js instead of the actual code.

u/yami_odymel — 3 days ago
▲ 24 r/codex

Sol Ultra audits everything so aggressively that I spend hours planning instead of writing code 🥀

Sol Ultra is very passionate about spawning subagents to audit the codebase and find edge cases.

I feel that when AI can do almost anything, the biggest question will be, "How much can you compromise?" Otherwise, there will always be something to fix.

I've been answering the plan Sol Ultra generated for nearly an hour, and I still haven't made a single code change. 💀

reddit.com
u/yami_odymel — 1 month ago

OpenRouter + DeepSeek V4 Pro is cheap, but people didn’t mention-

It took 90 seconds for a single tool_call and nearly 30 minutes to complete a task that GitHub Copilot + Sonnet 4.6 could finish in just 5 minutes. 💀

Sure, you can brew a coffee while waiting for the result, but you still have to wrestle with the output afterward, so...

If your time is also your money... I don't know, man

u/yami_odymel — 3 months ago
▲ 338 r/homelab

Was going to build Linux racks, but RAM and HDD prices went crazy this year, turns out Mac minis and portable drives were the cheapest option 💀

  • 2x 8TB - WD My Book
  • 3x Mac Mini M4 16G/256G
  • 1x BLUETTI AC2P 300W/230Wh as UPS

AC2P can power a single Mac mini M4 for at least 8 hours. No USB alerts for outages, but you can ping a wall-powered Wi-Fi router to detect power loss.

Mac mini M4 running a web, media, file server and local LLM (Gemma 4-E4B, ~30 tk/s) for my startup’s content recommendation system (social website).

The M4 is really power efficient, around 10-30W, but it handles almost everything I need.

WD My Book for file storage, with everything backed up to Backblaze Personal Backup ($9/month, unlimited storage). Only the wired external drive is backed up—no network mirror drives (based on Backblaze ToS).

u/yami_odymel — 4 months ago