I built a startup using Remix and The Epic Stack - Here's my experience
Back in May 2024, I joined a startup to build middle.ro - a platform to help 12th-grade highschool students in Romania learn better for their exams. The codebase was already started. Tech stack at the time:
Frontend:
- Next JS
- Typescript
- Tailwind
- Shadcn
- Vercel
Backend:
- Nestjs
- PSQL
- Typescript
- Hetzner
I initially started on the existent codebase, but gradually, I started to hate it since it was hard to debug some issues with server components when it was already in production and I hated how server components need to interract with client components. Shifting through Vercel logs wasn’t easy and neither helpful.
Also for a single request, this stack would go through 3 servers before responding to the user, so naturally it was slow.
Move to Remix + The epic stack
I was excited to try the epic stack finally. I have been a big fan of Kent’s work, and the principles and decisions on which he built the stack are solid.
What is the epic stack? Check out Kent’s talk on it here: https://www.epicweb.dev/epic-stack
The move itself was smooth. All I had to do is replicate the routes from NextJS and move the fetch requests from server components into the loader
functions and instead of requests to the backend server, the loaders made the same queries to the SQLite DB using Prisma.
The great things
Remix flat routes
I love remix flat routes. I admit it was confusing in the beginning but gradually I started to love them.
In comparison to the new NextJS routing where you need to build deeply nested folders, pages and layout files, Remix Flat Routes let’s you have a more sane structure.
I admit it can look weird to somebody who isn’t aware of them:
Ex:
cms+/essays.index.tsx
⇒ URL: cms/essays
cms+/essays/$essayId_.edit
⇒ URL: cms/essays/:essayId/edit
(The underline at the end of essayId_
means don’t inherit the layout from the route above it)
This system is more flexible than the NextJS system. One problem I found with NextJS is that you cannot escape a parent layout from a child so you need to put your child on another route or send the parent layout lower so it influences only the children it needs.
Speed of development
The speed of development is super high. You feel secure by the masterclass of tests Kent wrote for the epic stack so you push forward.
You can iterate very fast on UI using shadcn and handling data loading and mutation is a breeze thanks to Remix’s loader
and action
functions.
It takes a bit of time to get used to the lack of client side state management aside from cookies and url search params but once you get it, it’s very pleasurable to work with it.
Forms
The epic stack uses https://zod.dev/ and https://conform.guide/validation to handle forms. This provides full stack type safety through form schema validation and it is awesome.
The stack has existing examples of handling image upload and other complex form structures so you are covered in this area.
I built the entire CMS + integrating a full notion-like editor in less than a week and I was completely sure it had no bugs because of the type safety provided by zod + conform + prisma.
Note: This is a tree structure represented as a form that works even when users disable javascript in the browser
Tests
The stack contains tests for authentication, onboarding users, reset password flows, 2 factor authentication (yes this is implemented by default), handling emails and user’s creating notes.
Given the stack uses Remix it is very convenient to write E2E tests because you can do easy DB setup like creating and deleting users as fixtures for your tests.
For running the unit and E2E tests, the stack uses Playwright and Vitest.
Challenges
SQLite
The stack uses SQLite as a database. SQLite is the most used database in production. It’s fast, reliable, and easy to replicate given litefs and fly.io.
Given the epic stack ships inside a docker container and the DB is simply a file inside, it’s not easy to access the DB from other applications outside your web app.
The requirement here was we needed a CMS on top of the DB so somebody else could insert content through a user friendly interface.
There’s a thread on fly’s forum about exposing the sqlite file to the outside world in a secure way but in the end I chose to simply build a CMS inside the app which could handle complex relations between tables as oposed to normal CMS like directus where it can only show a pretty interface on top of tables
Seeding production
Seeding production was indeed a hastle to do. Kent provided good documentation for this part but it still takes a while until you get used to the process.
Other notes
Performance
One of the key advantages of using Remix and The Epic Stack is the excellent performance out of the box. The stack leverages Remix's server-side rendering capabilities, which results in fast initial page loads and improved SEO.
The Epic Stack also includes built-in optimizations like:
- Automatic code splitting
- Efficient asset loading
- Caching strategies
These optimizations contribute to a smooth user experience, especially on slower networks or devices.
One mention here: The stack starts you with 2 machines on Fly of 256MB of RAM and 1 shared CPU, because these are very cheap so you can try out the stack without getting a bill at the end of the month because you forgot to turn them down (hi AWS - I still can’t find that DynamoDB instance you charge me for)
If you release to production, I’ve found that the minimum machine size for optimum performance is 2 core CPU and 2GB of RAM. At the time of writing, for 2 of these machines you’ll pay 16$ / monthy.
Deployment and DevOps
The Epic Stack comes with a pre-configured deployment setup using Fly.io, which streamlines the process of getting your application into production. This includes:
- GitHub action to deploy once all tests and build passed
- Automatic database migrations
- Easy scaling options by adding new instances
The deployment process is well-documented and relatively straightforward, even for developers who may not have extensive DevOps experience.
It takes a bit of time to understand how primary and secondary nodes relationship works on Fly and what LiteFS does.
A great and free resource to understand the deployment setup is: https://www.epicweb.dev/tutorials/deploy-web-applications
Example: All mutations you do go to the primary node while reads go to the node closest to you.
More info here: https://fly.io/docs/litefs/
Learning Curve and Documentation
While The Epic Stack offers a lot of benefits, there is a learning curve to it, especially if you are new to Remix or some of the other technologies. However, the stack is well-documented, and there are numerous resources available in the Remix and Epic Stack communities.
The principles and decisions behind the stack choices are clearly explained, which helps in understanding the architecture and making informed decisions when customizing the stack for specific project needs.
If you decide to use the Epic Stack, a VERY good resource to understand it fully is Kent’s course, Epic Web Dev, specifically FullStack Volume 1.
Most of those workshops are built on parts of the epic stack so you will be very confortable with the structure
Monitoring
The stack comes with inbuild healthchecks that notify you if your application is down. It also comes with a done-for-you setup for Sentry so you can always inspect where your application crashes.
Conclusion
Finally, here is the final application deployed.
Overall, my experience with Remix and The Epic Stack has been overwhelmingly positive. I am fully convinced that if I ever need to build a web product again in Typescript or React, I will use the Epic Stack. It is aligned with my values of:
- No blackbox magic
- Use existing web standards
- Do things one way
- Offline first
I have been much more productive using this stack and I think it is the most complete free boilerplate out there.
For startups and projects that prioritise rapid development, robust features, and scalability, Remix and The Epic Stack provide a solid foundation that can adapt to various requirements and grow with your application.