The story begins—as it always does—with a tutorial.
I was watching yet another video and thought: “Let’s build a full stack app.” I already had a background in React, and I’d made a few full stack projects before. So this time, I chose Next.js. It looked easy, especially with the new App Router.
Why Next.js? Because it’s React at the front, backend APIs included, and server actions baked right in. No need for separate Express servers or wiring up extra routers. Also, the assumption was simple: everything is a server component unless you slap a "use client"
on top. I was in.
✨ Why I Fell in Love with the App Router
One of the best things about Next.js is the App Router. You don’t need to install React Router and define routes in App.jsx
. Just drop a file in the /app
folder, and boom—it’s a route. It's simple. It’s fast. It works.
🤯 First Battle: The UI Fear
Here’s my harsh truth—I suck at design.
My pages looked ugly. And that really messes with you. It triggers imposter syndrome. I’d open VS Code, freeze, and think: What do I even build here? The spiral begins: “Am I even cut out for frontend dev?”
Sound familiar? Been there. This time, I didn’t let it win.
Here's how I beat the block:
- Took breaks when stuck.
- Drew wireframes, even if they were ugly. Just enough to give me direction.
- Used inspiration from other websites. Seriously, 90% of the internet uses the same layout tricks. Why not learn from them?
- Tools like Google Stitch, v0.dev, and Bolt helped generate page ideas and inspiration.
Any progress is better than no progress. Remember that.
❤️ Server Actions
God bless Server Actions.
No more creating an API route or writing fetch calls and handling them manually. Need to read from the server? Just keep the page component as a server component, use a Server Action to grab the data, and pass it down to your client components. Done.
You can use server actions inside client components too, but there are caveats. I won’t pretend I know them all—this project was me figuring things out on the fly. So I won’t preach, but here’s what worked for me:
- Keep data fetching in server components.
- Use server actions to mutate or fetch data.
- If using forms, yes—you can still call server actions from client components.
🧠 How I Wrote Ugly Code—Fast
Not gonna lie. I was a messy developer in the beginning.
Every time I made a new page/component, I didn't care about reusability. Things got chaotic fast.
But I followed one rule: put every component inside a well-named folder. For example: /components/dashboard/Table.tsx
. Later, when I needed it somewhere else, I refactored it—added props, defined interfaces, added type safety—and made it reusable.
So... am I a great React developer? Maybe not.
Am I resourceful and fast? Yes.
Best case: smart developer.
Worst case: functional chaos.
💌 The Email Saga
Now I understand why dev’s complain about bad DX (Developer Experience) on Twitter.
Until now, I had a chill life—React + Vite
, everything worked like magic. But for this project, I needed to send emails inviting users to give feedback.
I initially tried Mailjet — looked promising, solid docs, decent limits. But then... everything broke after sending just 5 emails.
I got hit with a "temporary restriction" and no real explanation. Just a vague message and complete silence. I waited three days, hoping the ban would lift. Spoiler: it didn’t.
That’s when I realized how important good developer experience really is.
If I ever build a dev tool, I’m writing great docs, clear errors, and probably a fun YouTube guide.
So, I ditched Mailjet and moved to Resend — and this time, everything just worked. Fast setup, friendly DX, and no cryptic errors.
❤️ Huge shoutout to Resend.
🚫 No hard feelings, Mailjet — but that experience wasn’t it.
💀 The Broken Database Horror
After surviving all that, I wanted to brag on LinkedIn: “Hey look! I integrated payments!” Even if they were test payments—still, it’s progress.
But the horror unfolded quickly.
Understanding how payments work end-to-end—from initiation to confirmation—is critical. If you don’t, you’ll keep breaking your schema with every little change.
What I learned:
If you're dealing with third-party APIs or external systems, always:
- Track their status (
pending
,success
,failure
) - Use
enum
fields to reflect the state - Expect that some parts of the flow are out of your control
Know what you're storing and when. Or prepare to suffer. And yes, it will become a dev horror story.
🧹 Linting + TypeScript Woes
I used to think linting was just that one VS Code extension that makes code pretty.
Then I ran npm run build
.
Boom. 10,000+ lines of errors. Why? It tried to lint the auto-generated code too.
So yeah, I had to:
- Fix unused variables
- Replace
any
types - Update configs to ignore generated folders
Lesson learned: don't skip linting. TypeScript isn’t just for safety—it’s a build-saver.
🚀 Deployment & Production Nightmares
I’d spent a month building this project. Now it was time to ship. Resume-ready, you know?
I used Vercel. Guess what?
Vercel kept trying to render server pages as static. My build kept failing.
Turns out: Vercel will try to statically generate everything unless you tell it not to.
Solution:
export const dynamic = "force-dynamic";
Boom. Now it knows your page is meant to run server-side only.
Oh, and Supabase—my free DB. Connecting it was hell.
- There’s a pooled connection (for normal ops)
- And a direct connection (for migrations)
But Prisma didn’t play well with pooled URLs. It caused weird, unexpected issues.
Fix:
- Use Supabase’s "stable connection string" for local + migrations
- Use Prisma Accelerate in production with the correct URL
- Add the Accelerate URL to your
.env.production
After that, things worked. Kinda.
🧘♂️ Final Thoughts
You may not have learned any technical tricks from this post. But that’s the point.
Things will break.
Abstractions will leak.
You’ll be stuck. Frustrated. Confused.
But you’re a builder. And builders show up.
That’s what matters.
❤️ Takeaway
Everyone can teach you code. I want to teach you a mindset.
If you remember just two things from this post:
- Break things. That’s how you learn.
- Draw wireframes. Ugly ones. It gives you momentum.
That’s it. You’ll be 10x faster than me if you just start.
👋 Follow My Journey
I write about my dev rants, lessons, and experiments—sometimes messy, sometimes insightful. If this resonated, follow me. I don’t just rant—I teach too.
Next up: how I broke and optimized my backend.
Till then,
Break. Fix. Weep. Shine.
— Love and peace,
Your fellow dev
🎁 Farewell & Little Sendoff Gifts for Curious Minds
Thanks for sticking around! If you're curious to poke around or steal some ideas (I won't tell), here are a few goodies:
- 🔗 Live App:
- 💻 GitHub Repo: https://github.com/PRASHANTSWAROOP001/feedback-nextjs-app
- 🧾 Wireframes (Chaos Included): https://raw.githubusercontent.com/PRASHANTSWAROOP001/contact-page/refs/heads/main/wireframe2.jpg, https://github.com/PRASHANTSWAROOP001/contact-page/blob/main/wireframe1.jpg?raw=true
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.