vs. Next.js + Supabase
An honest comparison. No FUD, just reality.
The Footgun Catalog
Real issues beginners hit with the JS stack:
| JS/Next.js Problem | What Happens | PeARL Equivalent |
|---|---|---|
useEffect infinite loop |
App freezes, no obvious error | No useEffect—LiveView handles reactivity |
Forgot "use client" |
Cryptic hydration error | No client/server split—it’s all server |
| Stale closure in callback | Bug that only appears sometimes | Immutable data—no closures to go stale |
| State update in unmounted component | Memory leak warning spam | LiveView cleans up automatically |
| N+1 queries in RSC | Slow pages, hard to debug | Ash preloading is explicit |
| Vercel bill spike | $500 surprise invoice | Fly.io predictable pricing |
| Next.js upgrade breaking | Half your code needs rewriting | Phoenix upgrades are boring (good boring) |
| “Works locally, breaks in prod” | Environment differences | BEAM is the same everywhere |
| CORS errors | Confusing, random-seeming | Same-origin by default (WebSocket) |
| JWT expiration at midnight | Auth breaks mysteriously | Session-based, server-managed |
| Prisma migration conflicts | Database drift | Ecto migrations are linear |
“But I Already Know JavaScript”
This is the most common objection. Let’s be honest about it.
Reality: You’re learning three things pretending to be one.
With Next.js + React, you’re actually learning:
- React Client JS — hooks, state, effects, the rules of hooks
- Next.js RSC — server components, streaming, caching, the
"use client"boundary - Node.js API routes — different runtime, different mental model
The "use client" boundary alone is a whole new paradigm to understand.
With PeARL:
- React is React (you already know this)
- Elixir is Elixir (one new thing)
- The boundary is explicit:
pushEvent/handleEvent
The Three Runtimes Problem
Here’s what Next.js actually runs:
┌─────────────────────────────────────────────────┐
│ Your Next.js App │
├─────────────────────────────────────────────────┤
│ Browser JavaScript │ "use client" barrier │
│ (React, hooks, state) │ │
├─────────────────────────┼────────────────────────┤
│ Server Components │ Edge Runtime │
│ (streaming, RSC) │ (limited APIs) │
├─────────────────────────┴────────────────────────┤
│ API Routes (Node.js runtime) │
│ (full Node, different from Edge) │
└─────────────────────────────────────────────────┘
Now ask yourself: which runtime is your code running in? Are you sure?
PeARL’s answer:
┌─────────────────────────────────────────────────┐
│ Browser: React components │
│ (just rendering, no business logic) │
├─────────────────────────────────────────────────┤
│ Server: Elixir (Phoenix + LiveView + Ash) │
│ (one runtime, one language, all logic here) │
└─────────────────────────────────────────────────┘
The Vercel + Supabase Trap
These tools are optimized for starting, not sustaining.
Vercel
- Cold starts hurt latency (especially on free/hobby tiers)
- Bandwidth charges can spike unexpectedly ($500 surprise bills are common)
- Edge runtime limitations mean you can’t use all Node.js APIs
- Vendor lock-in is significant (try migrating away)
Supabase
- Row limits force tier upgrades at awkward times
- Realtime is expensive at scale
- Auth quirks (JWT expiration, session management)
- RLS policies are powerful but error-prone (easy to accidentally expose data)
Fly.io (PeARL’s default)
- Predictable pricing — you pay for VMs, not invocations
- No cold starts — your app is always running
- Simple mental model — it’s a VM, not magic
- Easy to migrate — no proprietary lock-in
“Isn’t Elixir Harder to Learn?”
Harder to start. Easier to understand.
JavaScript looks familiar but you’re constantly debugging:
- Why did this re-render?
- Why is this state stale?
- Why does
thispoint to undefined? - Why does this work locally but not in production?
Elixir looks unfamiliar but you can read it:
# Even if you don't know Elixir, you can read this:
def complete_order(order) do
order
|> validate_payment()
|> charge_card()
|> send_confirmation()
|> update_status(:completed)
end
Data flows top to bottom. No hidden state. No side effects surprises.
The Ecosystem Concern
“But npm has a package for everything!”
True. Consider:
- How many of those packages are maintained?
- How many have security vulnerabilities?
- How many will break on the next Node.js version?
- How many will break on the next React version?
PeARL’s approach: Fewer dependencies, each one battle-tested.
- Phoenix has been in production since 2014
- The BEAM has been in production since 1986
- PostgreSQL has been in production since 1996
- React has been in production since 2013
We’re not using bleeding-edge tech. We’re using boring technology that works.
When Next.js Is Right
To be fair, Next.js is a good choice when:
- Your team already knows it deeply (not just “knows JavaScript”)
- You’re building a content-heavy marketing site with hundreds of static pages
- You’re all-in on Vercel’s ecosystem (headless CMS, edge functions, etc.)
What about SEO? PeARL handles it. live_react supports full SSR:
# config/prod.exs
config :live_react,
ssr_module: LiveReact.SSR.NodeJS,
ssr: true
Your React components render server-side first (crawlable HTML for search engines), then hydrate on the client. Same pattern as Next.js SSR — without the three-runtime complexity.
PeARL is not the right choice for everyone. It’s the right choice for:
- Beginners who want guardrails
- Teams who value readability over cleverness
- Apps that need real-time features
- Founders who want to understand their own code
The Real Comparison
| Dimension | Next.js + Supabase | PeARL |
|---|---|---|
| Learning curve shape | Gentle start, constant spikes | Steeper start, then smooth |
| State management | You figure it out (Redux? Zustand? Context?) | Server owns it (LiveView) |
| Real-time | Bolt it on (Supabase Realtime, separate concern) | Built in (LiveView, same model) |
| Authorization | Add it yourself (easy to forget) | Deny by default (Ash) |
| Deployment | Vercel + Supabase (two bills, two vendors) | Fly.io (one bill, no lock-in) |
| AI assistance | Good React generation | Good React generation + readable Elixir |
| Debugging | “Which of three runtimes is this?” | “It’s on the server” |
| 6-month maintainability | “What does this useEffect do again?” | Read the DSL |
Make Your Choice
Neither stack is universally “better.” They optimize for different things.
Next.js optimizes for: Familiar syntax, fast starts, marketing sites, content.
PeARL optimizes for: Beginner completion rate, long-term readability, real-time apps, maintainability.
If you’ve been frustrated by the JS ecosystem’s footguns, PeARL might be worth the initial learning curve.
Next Steps
- Getting Started — Try it yourself