Articles by Anas Sheikh
Fix: useSearchParams() Should Be Wrapped in a Suspense Boundary (and Why It Only Breaks at Build)
You add a small search box to your navbar. It reads the query string, so you reach for useSearchParams(). Everything works in next dev. You push, the deploy fails, and the build log says: useSearchParams() should be wrapped in a suspense boundary at page "/about". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout Notice the page name. You did not touch /about. That detail is the whole story.
Your Server Action Error Messages Vanish in Production, and Throwing Is the Wrong Tool
Here is a bug report you will eventually get: "The signup form told me what was wrong on your machine, but on the live site it just says something went wrong." Nothing is broken. Next.js is doing this on purpose. But the code that triggers it looks completely normal, and it is in a lot of Server Actions tutorials, including a version in my own earlier post on replacing API routes.
Every Next.js Server Action Is a Public Endpoint, and Your bind(null, id) Is Not Protected
There is a belief that quietly causes real vulnerabilities in Next.js apps: "Server Actions are internal, so the ID I pass into them is safe." It is not. A Server Action is an HTTP endpoint with a generated URL, and anyone with a browser's network tab can call it with whatever arguments they want.
Your Next.js Page Might Be Caching One User's Data and Serving It to Everyone Else
This is the kind of bug that goes from "works perfectly in every test" to "actively leaking one customer's data to another customer" without a single code change in between, purely because of how caching and personalization interact in a way that isn't obvious from reading the component code.
Your Mongoose pre('save') Hook Is Being Silently Skipped by Half Your Update Calls
This is one of the more consequential Mongoose gotchas because of exactly what tends to live inside a pre('save') hook, password hashing, slug generation, validation logic, and exactly which methods silently bypass it without ever telling you.
Your Next.js Middleware Matcher Config Is Probably Skipping Routes You Think It Protects
Middleware feels like the cleanest way to gate an entire section of a Next.js app behind auth, one file, one config, covering everything under a path. That confidence is exactly the problem, because the matcher config that's supposed to define "everything under this path" has a few specific gaps that don't show up until someone hits a nested route that quietly skipped the check entirely.
Why revalidateTag() Does Absolutely Nothing for the Data You Just Changed
This one causes a specific kind of confusion because every piece of the code looks correct in isolation, you tagged a fetch, you called revalidateTag with that exact same tag string after a mutation, and the page keeps showing the old data anyway, like the cache invalidation call did nothing at all.
Your useEffect Search Bar Has a Race Condition, and It Only Shows Up When Users Type Fast
This is one of those bugs where the code looks completely reasonable, passes every manual test you'd naturally think to run, and still ships with a real defect that only shows up under a specific, very common usage pattern: someone typing faster than your network requests resolve.
placeholder="blur" on a Next.js Image Silently Does Nothing If You Forget One Thing
placeholder="blur" is one of those props that works perfectly the first time you try it, in a quick example with a locally imported image, and then quietly stops doing anything the moment you swap that example for a real image coming from a CMS, S3, or any other remote source.
Your Server Component Might Be Fetching Data Three Times Slower Than It Needs To
This is one of the easiest performance mistakes to write without noticing, since the code reads completely naturally, top to bottom, each line waiting for the one before it to finish, exactly the way most people learn to write async code in the first place.
That Third-Party Script Tag You Copy-Pasted Is Probably Hurting
Chat widgets, analytics snippets, ad network tags, review platform embeds, nearly all of them ship with a copy-paste <script> snippet meant for a plain HTML page, and pasted directly into a Next.js app without modification, several of them quietly do real damage to your actual performance metrics for content that has nothing to do with the widget itself.
Forgetting default.tsx in a Next.js Parallel Route Can 404 Your Entire Page, Not Just One Slot
This is a genuinely surprising failure mode the first time you hit it, since the natural assumption is that an unmatched parallel route slot would just render nothing, not take down a page that has nothing to do with the slot in question. Building on the intercepting route modal pattern from an earlier post, the @modal slot renders the modal when a specific route is intercepted.
Forgetting default.tsx in a Next.js Parallel Route Can 404 Your Entire Page, Not Just One Slot
This is a genuinely surprising failure mode the first time you hit it, since the natural assumption is that an unmatched parallel route slot would just render nothing, not take down a page that has nothing to do with the slot in question. The Setup From an Earlier Post, Extended Building on the intercepting route modal pattern from an earlier post, the @modal slot renders the modal when a specific route is intercepted.
Why Your useEffect Runs Twice in Development (And Why You Shouldn't "Fix" It)
This causes a very specific, very common reaction, a developer sees a console.log inside useEffect fire twice in the browser console during local development, assumes something's genuinely broken, and starts debugging a bug that doesn't actually exist.
That bcrypt.hash(password, 10) You Copy-Pasted Might Not Be as Secure as You Think
Almost every auth tutorial, including some of my own examples in earlier posts, uses bcrypt.hash(password, 10) without much explanation of what that number actually controls or why it's 10 specifically rather than some other value. It's worth understanding, because the right number isn't fixed, it's a moving target that most codebases never revisit after the initial copy-paste.
Using searchParams in a Next.js Page Silently Opts It Out of Static Rendering Too
I wrote earlier about a buried cookies() call silently forcing an entire page into dynamic rendering. searchParams causes the exact same category of behavior, through a completely different, and in some ways more surprising, mechanism, since using it doesn't look like reading anything server-specific at all. cookies() and headers() at least sound like they're reaching into something request-specific, so forcing dynamic rendering feels intuitive once you think about it.
Changing an Environment Variable on Vercel Doesn't Do Anything Until You Actually Redeploy
This causes a specific, recurring kind of confusion, someone updates an API key or a config value in their hosting dashboard, refreshes the live site expecting the new behavior, and sees the old value still very much in effect, with no error, no indication anything's wrong, just a value that appears to be stuck. Why This Happens Next.js resolves environment variables at different times depending on how they're used, and that distinction matters far more than it initially seems to.
Wrapping redirect() in a try/catch in Next.js Can Silently Swallow the Redirect
This one catches people specifically because the code looks like careful error handling, and it's actually the thing breaking the exact feature it's wrapped around.
The Real ROI of a Well-Built Website (Beyond "It Looks Nice")
"It looks nice" is the wrong frame for evaluating a website investment, and it's the default frame most business owners unconsciously use anyway, since design is the most visible, easiest-to-judge part of a site. The actual return on a well-built website comes from specific, measurable mechanisms that have very little to do with whether it looks impressive in a screenshot. This isn't an abstract technical concern, it's a documented, measurable relationship.
Why Your MongoDB Check in Next.js Middleware Might Be Silently Failing
I mentioned this briefly in an earlier post on middleware patterns, that direct database queries don't run reliably in middleware, and a few people asked what "not reliably" actually means in practice, since it sounds vague enough to ignore. It's worth a real, specific answer, because the actual failure mode here is genuinely easy to miss until it costs you something.
A Wildcard in Your next.config Image remotePatterns Might Turn Your Site Into an SSRF Proxy
next/image optimizing external images is genuinely convenient, resizing, format conversion, all handled automatically through Next.js's built-in image optimization endpoint. That endpoint fetches the remote image server-side before serving the optimized version to the browser, and that server-side fetch is exactly the mechanism that becomes a real problem if remotePatterns is configured too loosely.
Your useOptimistic UI Might Be Lying to Users When the Server Action Actually Fails
useOptimistic is genuinely one of the nicer additions to React for making an app feel fast, updating the UI immediately while the real request happens in the background. It's also very easy to implement in a way that only handles the success path, leaving the UI confidently showing something that never actually happened once the request fails.
Using an Array Index as a React key Can Leak Form State Between Completely Different Items
This is one of those React gotchas that's been written about for years and still shows up constantly in real code, specifically because it looks completely correct and works fine in the exact scenario most people test.
Your Next.js Website Feels Slow? Check These 7 Things Before Blaming the Server
Hello Glad to see you, I am Kane Lim from Hong Kong. I have over 10 years of development experience. I am writing this because your post was interesting. I strongly agree with the debugging order. One additional layer I would add is correlating browser telemetry with server traces instead of profiling each boundary independently.
Next.js Server Actions Have Built-In CSRF Protection. Your API Routes Probably Don't.
I wrote a post a while back about Server Actions being public, directly callable endpoints regardless of your UI, and a fair number of comments pushed back with some version of "sure, but at least they're not vulnerable to CSRF like a regular form post would be." That pushback is actually correct, and it's worth explaining exactly why, because the same protection does not automatically extend to a plain API route handler doing the same job.
Your MongoDB Login Query Might Be Vulnerable to a One-Line Bypass
Most developers coming from a SQL background know to worry about SQL injection instinctively, string concatenation into a raw query feels obviously dangerous. MongoDB's query syntax being actual JavaScript objects creates a different, less intuitive version of the same underlying problem, and it's genuinely easy to introduce without it looking wrong at all.
Calling redirect() Inside a Suspense Boundary Doesn't Undo What Already Streamed to the Browser
Streaming is one of the genuinely great things about the App Router, the browser can start receiving and rendering parts of a page before every single piece of data has finished loading. It also introduces a real ordering question that's easy to get wrong when auth checks and Suspense boundaries interact, and getting it wrong doesn't throw an error, it just occasionally shows a logged-out visitor a flash of something meant only for an authenticated user.
unstable_cache Can Silently Serve One User's Data to a Completely Different User
I wrote about how force-caching a user-specific fetch can leak data across users. There's a second, less obvious way to cause almost the exact same bug using unstable_cache, and it's worth covering separately because the mistake looks completely different on the surface, even though the end result, one person's data showing up for someone else, is identical.
Next.js vs WordPress for SEO in 2026 (An Honest Comparison)
This comparison comes up constantly with clients deciding how to build a new site, and the honest answer is neither option is universally better. Each one wins depending on what you actually need. Here is a real breakdown, not a framework sales pitch. Raw page speed. A Next.js site with Server Components and proper image optimization loads noticeably faster than a typical WordPress install, especially one running a dozen plugins.
One Buried cookies() Call Can Silently Kill Static Rendering for Your Entire Page
Here's a question worth actually checking on your own app: is that marketing page you assume is static actually being pre-rendered, or is something buried deep in its component tree quietly forcing it to render fresh on every single request? Calling cookies() or headers() anywhere in a page's render tree opts that entire route out of static rendering. Not just the specific component that called it, the whole page.
Next.js Server Actions Have Built-In CSRF Protection. Your API Routes Probably Don't.
I wrote a post a while back about Server Actions being public, directly callable endpoints regardless of your UI, and a fair number of comments pushed back with some version of "sure, but at least they're not vulnerable to CSRF like a regular form post would be." That pushback is actually correct, and it's worth explaining exactly why, because the same protection does not automatically extend to a plain API route handler doing the same job.
Check Your Next.js Client Bundle Right Now. Your API Keys Might Already Be There.
This one takes about ninety seconds to check on your own project, and I'd genuinely encourage you to stop reading and go do it before finishing this post, because if you have this bug, it's live in production right now, not a theoretical risk. Open your deployed site. Open dev tools. Go to the Network tab, find any loaded .js file from _next/static, and search it for the word sk_ or SECRET or the name of any API key you use. If you find one, that key is not hidden.
I've Reviewed a Dozen Next.js Stripe Integrations. Almost None Handle This Race Condition.
Quick fact that surprises a lot of developers the first time they hear it: Stripe does not guarantee it will deliver a webhook event exactly once, and it does not guarantee events arrive in the order they happened. Both of those are explicitly documented, stated plainly in Stripe's own docs, and almost every webhook handler I've reviewed on a client project or open source repo is written as if neither is true.
Stop Wrapping Everything in 'use client'. It's Quietly Killing Your Next.js Performance.
Open ten random Next.js App Router projects on GitHub right now. I'd bet real money that at least seven of them have a 'use client' directive sitting at the top of a page component that never needed to be there in the first place, quietly shipping way more JavaScript to the browser than the page actually requires. This is not a hypothetical problem. I've audited client projects where removing three unnecessary 'use client' directives cut the initial JS bundle by more than a third.
Next.js 15 Middleware Everything Beyond Auth Checks
Most middleware tutorials stop at "check if the user is logged in, redirect if not." That is a real use case, but middleware runs before every matched request, which makes it useful for a lot more than auth checks. Here is what else I actually reach for it. 1. What Middleware Actually Is Middleware runs on the Edge Runtime, before a request reaches your actual route, and before any rendering starts.
Error Monitoring in Next.js 15 with Sentry What I Actually Track
error.tsx` catches a failure and shows the user something reasonable. It does not tell you the failure happened at all unless you are actively watching. For a while my "monitoring" was a client messaging me that something was broken, which is not monitoring, it is finding out from the worst possible source. Here is the Sentry setup I actually use now, tuned to catch what matters without burying it in noise. 1.
Animations in Next.js 15 with Framer Motion The Patterns I Actually Use
Most animation advice online is either a flashy demo with a dozen effects stacked on top of each other, or nothing at all. What actually makes a dashboard or product feel polished is usually the opposite of flashy, small, consistent motion that makes state changes feel intentional instead of instant and jarring. Here are the patterns I actually reach for. 1.
Filtering, and Pagination in Next.js 15 The Pattern I Actually Use
Every dashboard eventually needs the same three things. A search box, a few filters, and pagination once the list gets long. The version I used to build kept all of that in useState, which meant losing your filters the moment you refreshed the page or shared a link with someone else. Keeping this state in the URL instead fixed both problems at once. Here is the setup. 1. Why URL Search Params, Not useState State in useState disappears on refresh and cannot be shared or bookmarked.
Stripe Payments in Next.js 15 The Setup I Use for Every SaaS
Every SaaS project eventually needs the same thing, a way to charge people. Stripe handles the actual payment processing, but wiring it into Next.js correctly, especially getting webhooks right, is where most of the real work sits. Here is the setup I use for checkout and subscriptions in every SaaS project. 1.
Testing Next.js 15 Apps — The Setup I Actually Use
For a long time I skipped testing on freelance projects entirely. Tight deadlines, one developer, seemed hard to justify. Then a client project broke in production because of a change that looked completely unrelated to the part that actually failed, and it cost more time to debug after the fact than writing tests would have taken up front. Here is the testing setup I use now, kept minimal enough that it does not slow projects down but catches the failures that actually matter. 1.
State Management in Next.js 15 Server State, Context, and When to Reach for Zustand
For a long time I reached for Redux on every project out of habit, even ones that barely needed client state at all. Next.js 15 changed that. Most of what used to live in a global store now belongs on the server, and the client-side state that is left over is usually small enough that Redux is overkill. Here is how I actually decide where a piece of state should live now. 1. Most State Does Not Belong on the Client Anymore This is the biggest shift from older React apps.
Authentication in Next.js 15 The Pattern I Use for Every SaaS
I used to reach for NextAuth on every project by default. Then I built a few dashboards where I needed full control over the token — custom claims, a specific cookie strategy, no extra abstraction layer to fight with. Rolling my own JWT auth turned out simpler than expected, and it's what I use now unless a client specifically wants OAuth providers out of the box. Here's the exact setup. 1.
MongoDB + Mongoose Patterns I Use in Every Next.js Project
Every MERN project starts the same way. You copy-paste a connectDB function from an old repo. You write a schema with no validation. Three months later you're debugging duplicate connections in production. Here's the exact Mongoose setup I use in every Next.js project now — no more guessing. 1. The Connection Caching Pattern Next.js reuses modules across hot reloads and serverless invocations. Without caching, you'll open a new MongoDB connection on every request.
How to Use Prisma with Next.js 15 — Complete Setup Guide
Prisma is the best ORM for Next.js projects. Type-safe queries, auto-generated types, clean migrations — it makes working with databases feel like working with TypeScript objects. Here's the complete setup.
Build Authentication in Next.js 15 From Scratch — No Libraries
Everyone reaches for NextAuth immediately. But if you want to understand how authentication actually works — build it yourself first. Here's complete JWT authentication in Next.js 15 with no libraries except jsonwebtoken and bcryptjs. What We're Building User registration with hashed passwords Login with JWT token Protected API routes Middleware to protect pages Token refresh 1. Install Dependencies npm install jsonwebtoken bcryptjs npm install -D @types/jsonwebtoken @types/bcryptjs That's it.
How I Built a Modern Agency Landing Page with Next.js 15 and Tailwind CSS
Anas Sheikh Full-stack developer building premium Next.js templates & web apps. Specializing in GSAP animations, cinematic UI, and SaaS dashboards. Selling on Gumroad. Location Pakistan Β· Remote Worldwide Work Full-Stack Developer & Template Creator at Pixel Anas Joined
How I Built a Premium AI SaaS Dashboard with Next.js 15, Tailwind CSS and Recharts
Anas Sheikh Full-stack developer building premium Next.js templates & web apps. Specializing in GSAP animations, cinematic UI, and SaaS dashboards. Selling on Gumroad. Location Pakistan ยท Remote Worldwide Work Full-Stack Developer & Template Creator at Pixel Anas Joined
How I Built a Cinematic AI Landing Page with GSAP, Canvas API and Next.js
A deep dive into building Digital Rain — a cinematic AI landing page template with falling math symbols, liquid glass UI, and GSAP scroll animations using Next.js 15 and Tailwind CSS. Most AI landing pages look the same. Hero section. Features grid. Pricing table. Footer.
What to Do When Your Developer Goes Silent Mid-Project
This is one of the more stressful situations in hiring freelance help, and it's common enough to be worth a real, practical answer rather than just sympathy. A developer who was responsive and reliable suddenly goes quiet, mid-project, with money already paid and work left unfinished. Here's what to actually do, in order.
revalidatePath Might Not Be Invalidating What You Think It Is
If you've ever called revalidatePath after a mutation, watched the data still show up stale on the page, and fixed it by just also calling revalidatePath on a few more related paths until it finally worked, you were probably fighting the actual behavior of the function rather than a bug in your own code. This one has a genuinely confusing default that catches a lot of people, myself included the first time I ran into it.
Show More
loading
Actions
Is this you?
As a journalist, you can create a free Muck Rack account to customize your profile, list your contact preferences, and upload a portfolio of your best work.Get in touch with Anas
Contact Anas, search articles and posts on X, monitor coverage, and track replies from one place.
Learn more about Muck Rack