Nex - A minimalist web framework for indie hackers and startups

Hello Elixir community!

I’m excited to share Nex, a web framework I’ve been building for the past few months. It’s designed for indie hackers, startups, and anyone who wants to ship web applications fast without enterprise complexity.

The philosophy:

Nex embraces simplicity and convention over configuration. It’s built for:

  • :rocket: Rapid development – Ship features, not prototypes
  • :bullseye: Indie hackers & startups – Build profitable products
  • :bar_chart: Internal tools & dashboards – Admin panels, data dashboards
  • :counterclockwise_arrows_button: Real-time applications – Live dashboards, chat, streaming with SSE
  • :globe_with_meridians: Server-side rendering done right – Modern web apps without JavaScript overhead

What makes it different:

  1. File-based routing – No configuration. Drop a file in src/pages/, get a route.
  2. HTMX-first – Build interactive UIs with server-side rendering. No JavaScript framework needed.
  3. Hot reload – Instant feedback while developing via WebSocket.
  4. Real-time ready – Built-in Server-Sent Events for streaming data.
  5. Zero config – Works out of the box with sensible defaults.
  6. Production-ready – Docker included, deploy to Railway/Fly.io/Render in minutes.

Quick example:

defmodule MyApp.Pages.Todos do
  use Nex.Page

  def mount(_params) do
    %{todos: fetch_todos()}
  end

  def render(assigns) do
    ~H"""
    <h1>My Todos</h1>

    <form hx-post="/add_todo" hx-target="#todos" hx-swap="beforeend">
      <input type="text" name="title" required />
      <button>Add</button>
    </form>

    <ul id="todos">
      <li :for={todo <- @todos}>{todo.title}</li>
    </ul>
    """
  end

  def add_todo(%{"title" => title}) do
    todo = create_todo(title)

    ~H"<li>{@todo.title}</li>"
  end
end

Get started:

mix archive.install hex nex_new
mix nex.new my_app
cd my_app
mix nex.dev

Visit http://localhost:4000 and you’re running.

What’s included:

  • File-based routing with dynamic routes ([id], [slug], [...path])
  • HTMX integration with automatic CSRF protection
  • Server-Sent Events for real-time streaming
  • JSON API support
  • Hot reload development experience
  • Production-ready Docker setup

Example projects:

I’ve included several examples to get you started:

  • chatbot – AI chat with streaming responses using SSE
  • chatbot_sse – Real-time streaming with HTMX SSE extension
  • guestbook – Simple guestbook with persistence
  • dynamic_routes – Comprehensive showcase of all routing patterns

What’s NOT included:

  • Built-in ORM (use Ecto directly)
  • Authentication system (integrate your own)
  • Asset pipeline (use CDN for Tailwind/DaisyUI)

This is intentional – Nex stays minimal so you can choose your own tools.

I’d love your feedback!

  • Have you tried it? What worked? What didn’t?
  • What features would make it more useful?
  • Any pain points with the current approach?
  • Would you use this for your projects?

Links:

Looking forward to hearing from you!

22 Likes

Nice, this looks great. I had a similar idea in mind. My initial thought is that I wish this used datastar instead of htmx.

2 Likes

I will think about it

2 Likes

Nice!

Not to start a war but my vote as a non-but-potential-user is for htmx (unless it supports both).

2 Likes

Glad to see this, I think we need more web frameworks!

WRT the above, my understanding is that these “hypermedia frameworks” (htmx maintains a list) are generally designed to interop with pretty much any standard backend, so you shouldn’t really have to worry about picking a winner.

1 Like

I’ve been waiting for something like this for a while. Great initiative! (I really wish Phoenix would have a less boilerplate code approach, and this is a great foundation)

1 Like

IMO it’s all about the learning curve. I suppose someone coming over from Next.js would prefer Nex’s same file-based routing concept. As for HTMX, I vote LiveView :slightly_smiling_face:

3 Likes

LiveView’s great (it keeps the roof over my head) but it’s not a one-size-fits-all (nor is any framework, though LiveView is a little more honest about that).

And I would say that file-based routing would be familiar to anyone who started back in the 90s writing spaghetti PHP/ASP/etc for who HTMX also feels familiar, no need to bring Next into the discussion :grin:

2 Likes

Love this discussion! The learning curve was definitely top
of mind when designing Nex - wanted to make it feel natural
for Next.js folks.

And hey, LiveView fans are welcome here too :grinning_face_with_smiling_eyes: We’re all
trying to make web dev better in our own ways!

4 Likes

Oh, so it is Next inspired, woops :joy:

EDIT: Oh, right, “Nex”… lol, doi me.

2 Likes

Kind of. They do similar things in different ways so you’d still need to rewrite things if you wanted to switch from one to the other.

Comparing solely htmx to datastar, htmx is less capable out of the box. Datastar = htmx + sse + alpine. All in a smaller footprint. I also think their default to fat morph is a nice DX win. Feels a lot more declarative.

Objectively I don’t see a good reason to chose htmx over Datastar but I can understand the subjective “I’m used to htmx”.

I also think they’re doing some interesting things with their web component lib, Rocket.

1 Like

Oh yeah, I just meant this framework doesn’t need to choose a winner. Of course as a dev you still have to pick :slight_smile:

As you know I have a strong preference for React-likes (a category in which I include LiveView even if it’s not quite there) so my opinion matters little here, but I’d probably choose HTMX solely on vibes. Datastar is open-core and the database world has taught me to be extremely skeptical of such things. Of course there are exceptions like Oban, but they are an exception because they have built community trust over a long period of time.

1 Like

FYI, you can have the best of both Elixir and React worlds by using Inertia Phoenix.

It lets you use React components as views from MVC, provides SPA-like navigation while still being server-driven.

3 Likes

MVC is not real and it cannot hurt you :slight_smile:

But yeah, you can always use React itself with Phoenix or I’m sure with Nex here as well. I am not particularly interested in React proper, though, only its model. LiveView’s model is similar except that it runs on the server and its design is not quite as careful.

I have grown quite wary of this layering of frameworks on top of frameworks without anyone actually understanding why things are the way they are underneath. That’s how you end up with accidental complexity, because those using and even designing the tools don’t remember what it was they were supposed to be doing.

There is something to be said for minimalism (and I hope the OP here is able to pull it off), but achieving a minimal design is a very careful and effortful process. React right after Fiber/Hooks is a good example of a design that was refined to near perfection over a long period of time. Since then idk what they’ve been up to and I don’t really care.

I briefly looked at the article. Is he trying to say that because modern “views” are highly interactive, we shall think there’s an MVC within MVC and get to a conclusion that the term itself makes no sense? It kinda does make sense to me, because I always think of MVC from a server-first apps perspective. A controller is a server action/endpoint to me. A model is the data that’s being transferred to views. A view is responsible for presenting that data to the end user and leaves them a choice to call other controllers.

1 Like

The author’s writing style lends itself to deep insights that only reveal themselves after weeks to months of repeated readings (at least in my experience, and I have seen others suggest the same). I would not recommend skimming his work as you are unlikely to glean much of value that way.

In this case, though, I was just making a joke about how MVC generally does not mean much of anything at all, a point underscored somewhere in the article.

1 Like

Nex looks neat!

Great initiative! (I really wish Phoenix would have a less boilerplate code approach, and this is a great foundation)

Periodic reminder that phoenix_playground exists and this is a full Phoenix LiveView app:

Mix.install([
  {:phoenix_playground, "~> 0.1.8"}
])

defmodule DemoLive do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0)}
  end

  def render(assigns) do
    ~H"""
    <span>{@count}</span>
    <button phx-click="inc">+</button>

    <style type="text/css">
      body { padding: 1em; }
    </style>
    """
  end

  def handle_event("inc", _params, socket) do
    {:noreply, assign(socket, count: socket.assigns.count + 1)}
  end
end

PhoenixPlayground.start(live: DemoLive)
15 Likes

Ironically @thiagomajesk is the author of the head on main rn :slight_smile:

Are there any gotchas for actually running such a script in production, or would that actually be fine? It is called “playground” after all.

3 Likes

Nice to see more web frameworks, and I have to agree with @garrison keep em coming!

Maybe next we shall have pure API one like FastAPI