HTML forms used to work before we decided everything needed to be a SPA. Then we spent years building elaborate client-side state management to recreate... HTML forms.
Ryan Florence watched this circus for long enough and said fuck it. Remix loads your data on the server before the page renders. Forms submit to the server like they did in 2003. No useState waterfalls, no useEffect soup, no debugging why your form submission triggered 3 re-renders.
// This is the whole fucking form
<form method=\"post\">
<input name=\"email\" />
<button>Subscribe</button>
</form>
Server gets the POST, validates the email, saves it, responds. Your JavaScript can be completely broken and the form still works. Mind-blowing concept in 2025.
What Actually Breaks When You Deploy
Your loader returns raw JavaScript objects instead of JSON responses. The browser gets confused, users see [object Object]
on the screen, and you spend 3 hours figuring out you forgot to wrap shit in json()
:
// This breaks mysteriously
return { projects: data }
// This works
return json({ projects: data })
FormData from multi-select inputs is pure hell. form.get('tags[]')
returns only the first tag, not all of them. Took me a full day to figure out you need form.getAll('tags')
instead. The error? "Tags is undefined" with no indication why.
AWS Lambda kills your loaders after 30 seconds with Task timed out after 30.00 seconds
. Vercel Edge Functions die after 10 seconds with FUNCTION_INVOCATION_TIMEOUT
. CloudFlare Workers timeout at 15 seconds with Error 1102: Worker threw exception
.
Your slow PostgreSQL query works fine locally with 50ms response time. In production with connection pooling and network latency it takes 12 seconds and dies with Error: Client has already been released back to the pool
.
The React Router v7 Shitshow
December 2024: They killed Remix as a brand and made it React Router v7. "Just change your imports" they said. Took three weeks to unfuck the migration. Every @remix-run/*
import broke. TypeScript threw Property 'loader' does not exist on type 'RouteObject'
errors in 47 different files.
The worst part? remix-utils
v7.6.0 completely broke with React Router v7.0.2. Every useSubmit()
call started throwing TypeError: Cannot read properties of undefined (reading 'submit')
. Took a week for the community package to catch up.
Docker builds failed because remix build
doesn't exist anymore - it's now react-router-serve build
or npx react-router build
depending on what phase of the migration you're in. CI/CD scripts that worked for months suddenly started failing with command not found: remix
.
Half the Stack Overflow answers still reference @remix-run/node
imports that don't exist. Even official Shopify blog posts haven't been updated.
Shopify Owns It Now
Shopify bought this in 2022 so it won't die. But the roadmap now serves e-commerce over everything else. Need better file upload handling? Too bad, Shopify cares more about checkout flows than your PDF upload form.
The good news: it runs anywhere. AWS Lambda, Vercel, CloudFlare Workers, your shitty DigitalOcean droplet. No vendor lock-in like Next.js pushing you toward Vercel every chance they get.
Why I Still Use This Shit
Because HTML forms work like HTML forms should work. Your data loads on the server where it belongs. You don't debug useState waterfalls or worry about hydration mismatches.
Until product asks for "real-time validation" and you end up rebuilding half of it as client-side React anyway. Then you question why you didn't just use Next.js like a normal person.