Hey everyone, I’m Prashuk. I spend most of my time building out full-stack apps with the MERN stack and Next.js.
I’m not a big fan of deep architectural theory unless it actually helps me ship a faster product. For me, if the UI feels sluggish or the "Lighthouse" score is tanking because of a massive Javascript bundle, that’s a problem I need to fix immediately.
Lately, I’ve been focusing a lot on code splitting. It’s one of those things that sounds complex but, once you get the hang of it, it completely changes how your app feels to the end user.
1. The "Aha!" Moment with Heavy Dashboards
I ran into this issue when I was building a pretty complex CRM dashboard a few months ago. I had all these heavy charts (using Recharts) and a massive data-grid component.
The problem? Even when a user was just on the "Settings" page, the browser was downloading the entire charting library. The initial bundle size was over 800kb. It was a mess.
What worked for me was moving away from standard imports and embracing next/dynamic. I realized that if the user isn't looking at a chart right now, the browser shouldn't be loading it.
2. Splitting at the Component Level
Most people know about route-based splitting (which Next.js does automatically), but the real magic happens at the component level.
In one of my projects, I had a heavy "Feedback Modal" that used a complex form validation library and some custom UI components. Instead of importing it at the top of the file, I did this:
import dynamic from 'next/dynamic';
const FeedbackModal = dynamic(() => import('../components/FeedbackModal'), {
loading: () => <p>Loading...</p>,
ssr: false, // I didn't need this on the server
});
// Now it only loads when the user actually triggers it.
By doing this for just three or four "heavy" components, I shaved about 150kb off my initial JS payload. It’s a huge win for performance with very little effort.
3. Dealing with Third-Party Libraries
Libraries are bundle killers. Specifically things like moment.js, lodash, or heavy animation libs like framer-motion.
I’ve learned the hard way: stop importing the whole library. If you only need one utility function, just import that function. Better yet, see if you can load the library dynamically only when a specific action happens.
In a recent project, I had a "Download PDF" feature using jspdf. Instead of having it in the main bundle, I moved the logic inside a dynamic import within the click handler. The user only pays the "performance tax" when they actually click the download button.
4. Server Components changed the game
With the Next.js App Router, we actually get a lot of this for free now. Server Components don't send their dependencies to the client.
My rule of thumb now is: Keep it on the server until you absolutely need interactivity.
If I have a huge Markdown parser, I run it in a Server Component. The client just gets the final HTML string. No heavy library sent over the wire. It’s a clean-code approach that also solves the performance bottleneck.
5. My Personal Best Practices
Over time, I've developed a bit of a checklist when I'm coding:
- Audit often: I use the
@next/bundle-analyzerregularly. If I see a big blob in the treemap, I find out who's responsible. - Skeleton Screens over Spinners: When you split code, the user will wait for a split-second. Make that wait look good with a nice UI skeleton.
- Don't over-split: Don't split a tiny 2kb component. The extra HTTP request overhead actually makes it slower. Focus on the big fish.
- Clean Code over Hacks: Keep your dynamic imports organized. Don't hide them deep inside logic; keep them at the top level of your component files so they're easy to find.
Conclusion
At the end of the day, users don't care about your clean folder structure or your fancy state management—they care if the page loads fast.
Modern code splitting isn't just about saving bytes; it's about being intentional with what you're asking the user's browser to do. Start small, look at your biggest components first, and you'll see a massive difference in how your Next.js apps perform.
Keep building!
— Prashuk