Hiring for Next.js is not really a framework decision. By the time you are comparing agencies, the question is whether the people you hire will make the right call on rendering, caching and hosting for each route, or default everything to dynamic and hand you a slow site with a React logo on it. Here is how we approach it, and when we would tell you to use something else.
Static, server rendered or revalidated: a decision per route, not per project
In the App Router every route segment is static until something opts it out. Reading cookies(), headers() or searchParams, or calling fetch with cache set to no-store, makes that segment dynamic, and the opt out propagates: one analytics helper reading a header inside a shared layout flips every page beneath it to server rendering on every request. You lose CDN caching for the whole subtree, and nobody notices until the origin bill or the TTFB graph moves. The build output says so plainly, and reading it is part of the job: after next build every route is listed as static, dynamic, or prerendered with revalidation. Any surprise in that table is a bug to fix before launch, not a curiosity to note.
The useful middle ground is incremental static regeneration. A product page with a revalidate window of an hour serves from cache and refreshes in the background, so visitors never wait on the origin. On demand revalidation with revalidateTag is better still when the CMS can fire a webhook: content goes live in seconds instead of waiting out a timer. For large catalogues, generateStaticParams prerenders the pages that matter while dynamicParams fills in the long tail on first request. The real trade-off is not technical elegance, it is how stale a given page is allowed to be. Stock and pricing: seconds. A blog post: a day. Decide that per route and most of the performance work is already done.
Where the server and client boundary sits decides what ships to the browser
A use client directive is not a file level annotation, it is a boundary: every module imported below it ships to the browser. The decay pattern is familiar. A component needs a piece of state, someone marks the whole page as client, the data fetching then has to move into useEffect because the server APIs are gone, and a page that used to arrive as HTML now arrives as a spinner followed by a request waterfall. Three or four of those and the framework is doing nothing you could not get from a Vite SPA with less machinery.
Keeping the boundary low is mostly discipline. Interactive pieces stay leaves: the filter panel, the date picker, the cart drawer. Data fetching stays in server components, close to the route that needs it. Providers are the case people get wrong: a theme or query provider does have to be a client component, but it can take children as props so the server rendered tree still streams through it untouched. Props crossing the boundary have to serialise, which rules out class instances and functions other than server actions, and a large object passed down is a large payload inlined into the HTML. Two habits catch most of it: run @next/bundle-analyzer before you ship, and wrap third party libraries that are not server component aware in a thin client shim instead of marking the consuming page as client.
Metadata, redirects and locales: where rebuilds actually lose traffic
Rankings rarely drop because of the framework. They drop because a migration shipped without a redirect map. Before touching anything, pull the full URL inventory from server logs, the existing sitemap and Search Console, write an explicit old to new mapping with 301s, pick one trailing slash policy and hold to it, and confirm that campaign query parameters still resolve. Chained redirects, and a canonical tag still pointing at the pre migration URL, are the two things we find most often when a site loses traffic after a rebuild.
On the framework side, generateMetadata runs per route and is async, so titles and descriptions can depend on the same data the page renders. Canonical and language alternates belong there, not in a hand maintained header. Structured data goes in the server component as a JSON-LD script, so it exists in the initial HTML rather than being injected after hydration. For images, the sizes attribute is what actually drives the generated srcset: get it wrong and a phone downloads a 1600 pixel file to display it at 380. next/font self hosts the files and kills the layout shift a third party font call introduces. For multi locale sites, one canonical per locale, a reciprocal hreflang matrix, and a clear rule about which content exists in which language beat an automatic translation layer every time.
Hosting and reversibility: the app should be able to leave the platform
Vercel is the smoothest place to run Next.js, and it should be, since the platform team writes the framework. For plenty of projects it is simply the right answer: there is an entry tier for personal projects, a Pro plan at roughly 20 USD per seat per month at the time of writing, and usage based charges on top for bandwidth, function execution and image optimisation. What we will not do is build something that cannot leave.
In practice that means standalone output, a Dockerfile and a deployment that runs on a plain Node host, tested before launch rather than discovered during an incident. Off platform, three things need attention. Incremental regeneration and the data cache need shared storage: with several containers and a local filesystem cache, each instance revalidates on its own schedule and users see different versions of the same page. Image optimisation needs sharp and real CPU, or an external optimiser, otherwise the first traffic spike pins the server. Middleware runs on Node rather than at the edge, so anything latency sensitive there needs rethinking. Costs vary by stack: managed Postgres platforms such as Supabase price by tier, with the first paid plan around 25 USD a month at the time of writing, and a modest VPS sits in the region of 5 to 20 EUR a month if you accept owning the operations. Either way the repository, the domain and the cloud accounts are in your name from day one, and no subscription to us is what keeps the site online.
When Next.js is the wrong tool, and when we are the wrong team
Next.js earns its complexity when you need server rendering for SEO, real interactivity and a codebase that will keep growing. Outside that it is overhead. A five page site that changes twice a year is better as static HTML, or on Astro, or honestly on a hosted builder such as Squarespace or Wix, where a monthly plan buys hosting, editing and a form handler and you never think about a build pipeline again. A shop whose team lives in the admin every day is usually better served by Shopify than by a headless build: the entry plans run to a few tens of dollars a month depending on the plan and the country at the time of writing, and they include checkout, tax handling and apps you would otherwise rebuild. A marketing team that wants to restructure pages weekly without a developer will be happier in Webflow or WordPress. An internal dashboard behind a login with no SEO requirement rarely justifies server components at all: a Vite SPA against an API is smaller and cheaper to maintain.
We are the wrong team in several cases too, and it is cheaper to know that now. If you need meetings, video calls or someone in the room, the answer is no, permanently. If you need an on call rota with a contractual response at 3am, we do not sell it. If you need eight developers to hit a fixed date, we are two. If you want a partner to own the product decisions rather than execute them well, hire a product studio. And if you already have a capable in house team that just needs extra hands billed by the hour, a freelance marketplace will serve you better.
A two person team in Dijon, working entirely in writing across time zones
We are two people: Robin Monteiro on the code, Next.js and React, and Florian Loppion on the marketing side. One office, at 9 rue Jean-Jacques Rousseau in Dijon, France, and no others. There is no account manager between you and the person writing the code, and no pretence of being larger than we are. Our named references are French SMEs: Chouchou Ribeyre, Au Petit Detail, LB Athletic, Mediavocats and Vectosolve.
Everything runs by email. No calls, no video, no scheduled slots, not as a preference we bend for large accounts but as the way the work is organised. For a client in California or Bangalore that removes the scheduling problem entirely: you write when it suits you, we answer within 24 business hours, and nobody sits on a 3am call to approve a caching change. It also leaves a record. Six months later, the reason a route was left dynamic sits in a thread you can search, not in somebody's recollection of a Tuesday call.
You do have to work differently. An ambiguity a call would clear in thirty seconds costs a round trip in writing, so the first message matters: the URL, what you expected, what happened, a screenshot, the constraint behind the request. A client who writes one precise paragraph instead of three vague lines gets a usable answer in the first reply rather than the third. Pricing is on request, there is no mandatory subscription, and the code, the accounts and the domain stay yours.