Hire Next.js Developers in India
If you are about to hire Next.js developers in India, the thing to screen for is not component skill. It is whether the person knows where each line of their code runs, what it costs when that answer is wrong, and which of the caching layers is lying to them today. This page is the screening guide, written for the person doing the hiring.
What Actually Changes When You Hire for Next.js Instead of React?
React is a view library. Everything difficult about it happens in one place, the browser, and the questions are about state ownership and render behaviour. Next.js takes that same component model and drops it into a framework that owns routing, a server runtime, a build pipeline, several caches and, in most shops, the deployment target too. The component code looks almost identical. Nearly everything around it is a different job.
This is why the two roles should not be written as one line in a job advert. A React engineer is judged on whether your application is still changeable at fifty screens. A Next.js engineer is judged on that plus a second axis: does the right thing render in the right place, at the right time, with the right freshness, at a cost you are willing to pay every month. Those are infrastructure questions wearing a frontend hat, and plenty of strong React people have never had to answer them.
If the browser layer is genuinely your whole problem, our page on how to hire React developers in India covers that screening in detail, including state management and rendering performance. Come back here when the words server, cache or deploy start appearing in the same sentence as your requirements.
Why a strong React developer can be actively dangerous in Next.js
This sounds like a cheap line and it is not. A React specialist arrives with a set of instincts that were correct in a client-rendered application and are wrong here, and because the code compiles and the page loads, nothing tells them.
The instinct to reach for useState and useEffect the moment a component needs data. In the App Router that turns a component that could have fetched on the server into one that ships JavaScript, waits for hydration, then makes a round trip the server had already made. The instinct to add "use client" as soon as something needs an event handler, which is fine locally and catastrophic when the component in question is a layout. The instinct to reach for a global state library to share data between routes, when the router and the server can already do it. The instinct to read a cookie during render because that is how you did it in the browser, which quietly opts the whole route out of static rendering.
None of these produce an error message. They produce a site that is slower and more expensive than the one you were promised, and the regression usually arrives in pieces over a quarter rather than in a single bad pull request. That is why it needs to be screened for at hiring rather than caught in review.
The three questions underneath every Next.js decision
Strip away the API surface and a Next.js engineer answers the same three questions all day. Where does this run: build machine, server at request time, or browser. How long may the result be stale, and what event makes it fresh again. Who pays for it, in latency for the user and in compute on the bill.
A candidate who can hold all three at once is the hire. One who can only answer the first is a React developer with a Next.js project on their CV, which is a perfectly reasonable thing to be, as long as you know that is what you are buying and you have somebody senior to catch the rest.
The App Router and Server Components: the Shift the Whole Hire Turns On
Everything else on this page is downstream of one idea: a component now has a location, and the location is a design decision rather than an accident of where the file sits.
What a Server Component actually is
In the App Router, components render on the server by default. They can be async functions. They can await a database query, read a file, call an internal service with a credential, and return markup. The libraries they used to do that never reach the browser, which is the part people underestimate: pulling a heavy formatting or parsing dependency into a server component costs your users nothing at all, while the same import in a client component is bytes on the wire and time on the main thread.
What they cannot do is anything interactive. No state, no effects, no event handlers, no browser APIs. They render once, produce output, and are finished. A candidate who describes this as a limitation has understood it; one who describes it as a new kind of component has not, because the point is that most of your tree never needed to be interactive and you were shipping it to the browser out of habit.
The client boundary, and where people put it wrong
The "use client" directive is not a label you attach to the components that need interactivity. It marks a boundary in the module graph. From that module downward, everything imported is compiled for the browser, whether or not it uses a single hook. Someone who adds the directive to a shared layout because the navigation has a dropdown has just moved the entire page into the client bundle, and the build will not complain.
The technique that separates people who have shipped this from people who have read about it is composition through children. A client component can receive server-rendered content as children or as any other prop holding elements, and that content still renders on the server. So the interactive shell goes on the client and the expensive tree stays where it was. Ask a candidate to describe how they would build a collapsible section whose contents come from the database. If the answer moves the whole section to the client, keep interviewing.
The correct instinct is to push the boundary as far down the tree as it will go, and to keep leaf components small enough that going client costs almost nothing. In review, a new "use client" near the root of a route is worth a conversation every single time.
What can and cannot cross the boundary
Props passed from a server component into a client component have to survive serialisation. Plain objects, arrays, strings, numbers, dates and the usual collections are fine. Functions are not, with the single exception of a Server Action, which is compiled into a reference rather than shipped as code. Class instances do not survive either, which surprises teams whose data layer returns rich model objects from an ORM. The pragmatic fix is to map to a plain shape at the boundary, and it is worth doing deliberately rather than sprinkling JSON.parse(JSON.stringify(x)) around, which is the version you will find in a rushed codebase.
The other direction matters more for safety. Anything reachable from a client module ends up in the browser bundle, including the module that holds your data access code and, potentially, whatever it reads from the environment. The guard for this is the server-only package: import it at the top of a module that must never be bundled for the browser and the build fails loudly the day someone imports it from the wrong side. It costs one line and it turns a security question into a build error. Candidates who already do this have been burned once.
Codebases still on the Pages Router
Plenty of profitable applications are on the Pages Router and there is no emergency. Both routers coexist in one project, so routes move across when they are being changed for other reasons. What matters for hiring is which direction the candidate has actually travelled. Someone who has only ever written getServerSideProps knows a real thing and does not yet know this one. Someone who has migrated routes incrementally has met the awkward parts: two layout systems in one application, shared auth logic that assumed a request object, and the temptation to port a data-fetching function literally instead of rethinking where it runs.
That last one produces the most common migration smell in the ecosystem. A server component that calls fetch against the application's own API route, on the same server, because that is what the old page did. It works. It also adds a network hop and a serialisation round trip to something that could have called the data layer directly. If you see it in a take-home, ask about it, and listen to whether the answer is a defence or a laugh.
Data Fetching and the Caching Layers That Catch People Out
If you only screen for one thing beyond the client boundary, screen for this. Most Next.js bugs reported by a business as a content problem are cache problems, and they are stacked, which is what makes them hard.
Request memoisation
Within a single render of a single request, identical fetches are collapsed into one. That is what lets you call your getCurrentUser helper in a layout, in a page and in three components without thinking about it, instead of threading the result down through props. It lives for the lifetime of that render and is not shared between users or requests. It is the least troublesome layer and the one candidates most often forget exists, usually revealing themselves by prop-drilling data that did not need drilling.
The data cache
This one persists across requests and across deployments, on the server. It is the layer that makes a page fast for the second visitor. It is also the layer whose default behaviour has changed between major versions of the framework, which is exactly why you should never let a candidate assert the default from memory and should check the documentation for the version you are actually running. The right answer at interview is that they set the intent explicitly per call rather than relying on whatever the default happens to be this year.
The important operational property is that it is keyed on the request, not on your business objects, so invalidating it means either waiting out a time window or tagging the fetch and invalidating the tag when the underlying thing changes. Teams that skip tagging end up choosing between content that is minutes stale and a page that never caches at all.
The full route cache
A statically rendered route has its rendered output stored at build time and served without running your code. This is where the speed comes from and it is also where the phrase "but I published it" comes from. Nothing in your CMS knows that a build happened. Unless the route revalidates on a timer or something calls a revalidation with the right path or tag, that HTML is what visitors get until the next deploy.
The client router cache
The layer that produces the strangest bug reports. As a visitor navigates within the application, the router keeps the payloads it has already fetched in memory for the session, so going back to a page you just left is instant. That means a user can complete a form, navigate back to the list and see their old data, while a hard refresh shows the correct thing. Testers report this as intermittent. It is not intermittent, it is the difference between a soft navigation and a document load. The fixes are to refresh the router after a mutation or to revalidate the path the mutation affected, and the configuration around how long these entries stay fresh has moved between releases, so check the current documentation rather than a blog post from two years ago.
Revalidation, and how to choose between the three ways
Time-based revalidation suits content whose staleness is measured in minutes and whose publishing you do not control, such as a feed from someone else's system. Path revalidation suits a mutation whose blast radius you know exactly: this order was updated, therefore this order page is wrong. Tag revalidation suits everything else, and it is the one that scales, because a piece of content can carry a tag that a dozen unrelated routes all depend on and one call fixes all of them.
The mental model to test for is stale-while-revalidate. With time-based revalidation, the first request after the window expires still receives the old page while regeneration happens behind it. So the editor who publishes, reloads immediately and sees the old content is not looking at a bug, and the second reload will be right. If your content team does not know this, you will receive that ticket monthly. It is worth writing down for them in plain language during onboarding, and it is worth asking a candidate to explain it, because the ones who have supported a live site explain it in one sentence.
Where this goes wrong on a real team
Two failure shapes, and they are opposites. The first is a team that hit one stale-content incident, panicked, and turned off caching everywhere: every route dynamic, every fetch uncached. The site now recomputes the same page for every visitor, the response times climb under load, and if you are on usage-based hosting the invoice goes with them. The second is a team that cached aggressively without any invalidation path, so freshness is a deploy. Both are the same missing skill, which is deciding per route how stale the business can tolerate this being and wiring the invalidation to the event that changes it.
Rendering Strategies, and What It Costs to Choose the Wrong One
Per route, sometimes per component. The choice is rarely wrong on purpose. It is usually made by accident, three imports away, by somebody who was solving a different problem.
Static, and the accidental exit from it
A route renders at build time when nothing in it demands request-specific information. Reading cookies or headers, using search parameters, or calling any of the framework helpers that require the incoming request moves the route to dynamic rendering, and it does so for the whole route rather than the component that asked. This is the mechanism behind the screening question further down. A shared analytics wrapper or an auth helper deep in a layout reads a cookie, and forty static marketing routes quietly become server-rendered.
The diagnostic is the build output. Next.js prints a table of every route with a symbol for how it was rendered, and reading it is a five second check that most teams never make part of their pipeline. Getting a candidate to say "I would look at the build output" unprompted is a strong signal, because it is the habit of somebody who has debugged this rather than read about it.
Dynamic rendering, chosen deliberately
Sometimes correctness beats speed and dynamic is simply right. A logged-in dashboard, a basket, a price that depends on the visitor's country, anything personalised. The mistake is not using dynamic rendering, it is using it for a page where nearly all of the content is identical for everyone and one strip at the top says the user's name. That page wants a static shell with the personalised part isolated, either behind a streaming boundary or rendered on the client after load.
Streaming, and the boundary that does nothing
Streaming lets the server send the parts of a page it already has while a slow dependency resolves, so the visitor sees layout and content instead of a blank document. A loading file at a route segment gives you this for the segment, and Suspense boundaries give you it anywhere you want.
Two mistakes, and I have seen both in production code. The first is one enormous boundary at the top of the route, which replaces the whole page with a spinner and is worse than what the team had before. The second is subtler and more common: awaiting the slow call in the layout, above the boundary, so nothing streams at all and the developer concludes streaming does not work. The slow await has to sit inside the boundary. Ask a candidate what they would put a boundary around on a product page where the reviews service is unreliable and you will learn a lot in ninety seconds.
Incremental regeneration for content that changes on its own schedule
Static output with a revalidation window is the right default for most published content: documentation, articles, catalogue pages, anything a CMS produces. Visitors get files, your database is not touched, and freshness is a policy rather than an accident. The operational catch is the one described above, the first visitor after expiry seeing stale content, plus a self-hosting wrinkle covered later on this page.
Partial prerendering, and how to talk about it
The direction of travel is a static shell served instantly with dynamic holes streamed into it, so a page stops being one strategy and becomes a mixture. It is genuinely the most interesting idea in the framework right now. It has also spent time behind experimental flags and its status moves, so do not let a candidate present it as settled, and do not commit a roadmap to it without reading the current documentation yourself. What you want to hear is that they understand the shape of it and know where to check whether it is ready for your risk appetite.
Server Actions, Mutations and the Security Question Nobody Asks
Actions removed a whole layer of boilerplate. They also moved a security boundary to a place that does not look like one, which is worth an explicit interview question.
What an action is underneath
Mark an async function with the server directive and you can call it from a client component as though it were local. What is really happening is that the build creates an endpoint, the call becomes a POST carrying the arguments, and the result comes back. It can be wired straight to a form element so the mutation works before any JavaScript has loaded, which is a genuine progressive enhancement win and a nice detail to hear a candidate bring up unprompted.
Why it is a security boundary
Because it is a public endpoint. It has no route in your file tree, the URL is opaque, and none of that is protection. Anyone can send arbitrary arguments to it, in any order, from anywhere, with a session that may or may not be entitled to what they are asking for.
So the rules are the ones you would apply to any API. Validate the input inside the action, with a schema rather than a few if statements, and treat every identifier arriving from the client as a claim rather than a fact. Load the session inside the action and check the permission there. The failure I look for in code review is an action that trusts the caller because the button that calls it is only rendered for admins, which is not authorisation, it is decoration. Ask a candidate directly where the permission check goes. If the answer involves the component that renders the button, that is your answer about their backend experience.
Finishing the mutation properly
A mutation that changes data and does not tell the framework leaves the user looking at what they just changed, unchanged. The action should invalidate the tag or the path that the mutation affected, and where the user is about to navigate, the client cache needs a refresh too. This is the number one source of "it saved but it did not save" reports, and it is entirely preventable by treating invalidation as part of the mutation rather than a follow-up ticket.
When a route handler is the better tool
Actions are for your own interface. A route handler is the right answer when the caller is not your browser: a payment provider's webhook, a mobile client, a partner integration, a cron job, anything that needs a documented URL and a stable contract. It is also the right answer when you need response streaming or a non-JSON payload. A candidate who reaches for actions to build a public API has not thought about who is calling. If your API surface is large enough to be a project of its own, that is a backend hire in its own right, and our page for teams who hire Node.js developers in India covers what that looks like.
Middleware, the Edge Runtime and the Limits People Discover Late
Middleware is the smallest file in most Next.js projects and it is responsible for a disproportionate share of the incidents.
What it is genuinely good at
Middleware runs before a request is served, on paths you choose with a matcher, and it can rewrite, redirect, set headers and set cookies. That makes it the right tool for locale detection, marketing redirects after a URL restructure, bucketing visitors into an experiment, geo-based routing, and a cheap gate that bounces obviously unauthenticated traffic before it costs you a render.
The word to hold on to is cheap. Whatever you put in there executes on every matched request, and a careless matcher will match static assets and prefetches too. Work added there is work added to every page view on the site, including the ones that were otherwise being served straight from cache.
What the edge runtime will not do
Middleware has commonly run in an edge runtime built on web APIs rather than Node. That means no filesystem, no raw sockets, and a large part of the npm ecosystem simply not working, particularly database drivers and anything depending on Node built-ins. Bundle size limits apply too. The consequence people meet at the worst moment is that their existing session library, which reads from Postgres, cannot be used there, and they discover it when they deploy rather than when they write it.
Support for running middleware in a Node runtime has been moving, which is precisely the kind of thing that dates a page like this one. Treat the constraint as real until you have checked the documentation for your version, and ask a candidate what they would do if a library they needed did not run at the edge. Good answers include verifying a signed token with a web-crypto based library instead of hitting the database, or moving the check out of middleware entirely.
The authorisation mistake
The mistake is treating middleware as the security perimeter. It is a routing layer. It sees a path and some headers, it does not know what your page is about to read, and any layer that makes decisions from request metadata is a layer where bypasses get found. Header-driven routing has a long history of exactly that across the whole industry, not only in this framework.
Defence in depth is the position to hire for. Middleware may redirect a visitor without a session cookie, because that is a good user experience and it saves compute. The real check belongs where the data is read: in the server component, in the action, in the data access function, close enough to the query that nobody can route around it. Ask a candidate whether they would be comfortable if middleware were removed entirely and the application still had to be safe. The confident yes is the one you want.
Images, Fonts and the Two Vitals That Get Blamed on the Framework
Two built-in components carry most of the loading performance of a typical Next.js site, and both have a default that is wrong for the one element that matters most.
The image component and the hero image trap
The image component generates a set of sources at different widths, serves a modern format where the browser supports it, reserves the space so the layout does not jump, and lazy loads by default. That last default is correct for everything below the fold and wrong for the largest element above it. Forgetting to mark the hero image as a priority load is the single most common reason a Next.js marketing page has a poor Largest Contentful Paint, because the browser now waits to discover the image instead of preloading it.
The second most common reason is a missing or careless sizes attribute, which leads to a phone downloading an image sized for a desktop. Ask a candidate what sizes does. Anyone who has fixed a slow page will answer immediately; anyone who has only used the component will not know it exists.
Optimisation itself has to happen somewhere. On a managed platform it is a service you do not think about until it appears on the bill. Self-hosted, it is a resizing library doing CPU work in your container, or an external loader pointing at a CDN that does the transformation for you. That decision belongs in the architecture conversation, not in the sprint where somebody notices the container keeps restarting.
Fonts, and the layout shift nobody attributes correctly
The font component self-hosts the font files at build time, so there is no request to a third-party font host on the critical path and no extra DNS lookup and connection. More usefully, it generates a fallback whose metrics are adjusted to match the real face, which is what stops the page reflowing when the web font arrives. Cumulative Layout Shift on content sites is very often that reflow plus a skeleton that is not the height of the thing replacing it.
The failure to watch for is a team importing five weights and three styles because the design file listed them, then loading all of them on every route. A variable font, or two weights chosen deliberately, is the usual right answer.
What actually moves Interaction to Next Paint
Interaction to Next Paint is the vital that Next.js applications fail, and the framework is usually not the culprit. Shipping a large client bundle because the boundary sits too high, hydrating a heavy tree while the visitor is already clicking, and re-rendering a large subtree from a controlled input are the three causes I see repeatedly. All three come back to the same discipline: keep the interactive surface small and let the server do the rest. A candidate who connects a poor field measurement to the size of the client tree, rather than to a missing memo call, is thinking about this correctly.
Vercel or Self-Hosted? These Are Two Different Jobs
This is the part of the brief teams forget to write down, and it changes which candidate you should hire more than any framework question on this page.
What the managed platform is doing while you are not looking
Deploy to Vercel and a long list of things are handled without ever entering your architecture diagram. Incremental regeneration and its cache. Image optimisation as a service. Middleware placed near the user. Routes split into functions automatically. Preview environments for every branch. A CDN in front with sensible cache headers already set. Atomic deploys where old and new assets both keep working during the switch.
None of that is a criticism. It is a very good product and for a large number of teams it is exactly the right call, because none of those things are your business. The point is that an engineer whose entire Next.js career has been on that platform has never had to know any of it exists, and their CV will not tell you that.
What you take on when you leave
The regulated or security-driven move into your own cloud is the common trigger, and it is the moment several invisible things become tickets. The first is the regeneration cache. By default it is on the local filesystem, which works perfectly on one machine and falls apart across several: instance A regenerates a page, instance B keeps serving the old one, and your traffic split decides what each visitor sees. The fix is a shared cache handler backed by something like Redis, configured explicitly. On-demand invalidation has the same shape of problem, because a revalidation call arriving at one instance has to be visible to all of them.
Then image optimisation, which needs the resizing library present and enough CPU headroom, or an external loader so somebody else does the work. Then middleware, which needs the runtime you actually have. Then the CDN in front, where the rule is that hashed build assets can be cached hard and forever while HTML must not be, and getting that backwards is how a deploy leaves half your users on the old JavaScript. Then log aggregation, health checks, graceful shutdown, and a build pipeline that produces the minimal standalone output rather than copying a full node_modules into a container image.
That is an infrastructure project, and it is why we usually staff it as one. A Next.js engineer who has done it once is worth a great deal here; if the work is substantial, pairing them with someone from the platform side is the honest structure, and our page for teams who hire DevOps developers in India describes that half of it.
How to ask the question in an interview
Do not ask whether they have self-hosted, because the answer is always yes. Ask what broke. The people who have genuinely done it will tell you about the cache handler, or about images pegging the CPU on a marketing launch, or about a middleware library that would not build. The people who ran a container locally once will describe the Dockerfile.
How Do You Screen a Next.js Developer in an Hour?
Four questions, each with a wrong answer that sounds professional. Between them they cover rendering, the client boundary, caching and judgement, which is the whole job.
A page that should be static is rendering dynamically. Why?
The best possible first response is a question back: how do you know, and did you look at the build output. From there, a good candidate walks the causes. Something in the route reads request-specific data, and it is usually not in the file you are looking at but in a shared component, an analytics wrapper or an auth helper several imports away. A dynamic export left in place by whoever was debugging last month. A fetch marked as never cached, or a data layer call that touches the request. A third-party component that reads headers on your behalf.
Then listen to the fix. The weak fix is forcing the route static, which either breaks the thing that needed the request or silently caches something personalised, and that second outcome is a data leak rather than a performance bug. The strong fix isolates the dynamic part: move it below a streaming boundary so the rest of the page stays static, or move that piece to the client, or handle it in middleware where it is a routing concern rather than a rendering one.
What happens when a client component imports a server-only module?
This question is a scalpel. The mediocre answer is that it will not work. The accurate answer describes what actually occurs, which depends on the module. If it touches Node built-ins, the build fails with a resolution error and you have got off lightly. If it is pure JavaScript that reads secrets from the environment, it compiles cleanly and the values are simply absent in the browser, because only variables carrying the public prefix are inlined into the client bundle.
Then the sting, and this is what I am really listening for. The developer who does not understand the mechanism fixes the undefined value by adding the public prefix to the variable name, and now your API key is in a JavaScript file served to everybody. The candidate who has internalised the boundary will say this out loud without prompting, and will then tell you about the server-only guard that turns the whole class of mistake into a failed build. That is a senior answer.
You deployed, and the page is still stale. How do you debug it?
What separates people here is whether they have a method or a ritual. The ritual is redeploy and clear the cache. The method walks the layers from the browser inwards.
Is this a hard load or a soft navigation, because the client router cache explains the second and not the first. Was the route static at build time, which the build output tells you. Did the publish actually trigger a revalidation, which means checking the webhook or the action that was supposed to fire and not assuming it did. Was the underlying fetch cached with a tag, and does the tag being invalidated match the tag that was set. Is there a CDN in front holding the HTML, and what do the response headers say about who served it and whether it was a hit. On a self-hosted cluster, did the invalidation reach every instance or only the one that received the call, which you find out by hitting the same URL repeatedly and watching the answer change.
A candidate who mentions response headers and the build output has debugged this on a real site. A candidate who says they would clear the cache has not been the person on the call with the marketing team.
When would you not use Next.js?
Ask this last and treat a blank look as disqualifying, because an engineer who cannot argue against their own tool will apply it to everything you own.
The answers I find credible: an internal authenticated tool where nothing is indexed and server rendering buys you nothing, which is simpler and cheaper as a client-rendered application on a static host. A pure content or documentation site where a lighter static generator does the job with less machinery. An application embedded inside someone else's page, where a framework that owns routing is in the way. An organisation with no appetite for running a Node tier, where adding one is a new operational surface with on-call attached. And the honest team answer: if nobody here will own the server side, the framework becomes a liability the first time caching bites.
Two smaller questions worth adding
Ask about a hydration mismatch they have fixed. The Next.js flavours are formatting a date or a currency differently on server and client, reading a browser-only global during render, and rendering something based on a random value. What you want is a story with a diagnosis in it, not a definition.
Ask how they type the boundary between server and client, if you are on TypeScript, because the props crossing that line are a contract and typing them properly catches the serialisation problems at compile time rather than at runtime. Depth here varies enormously between people who list the language, which is why we screen it separately for teams who hire TypeScript developers in India.
Three Next.js Situations That Keep Recurring, and Who to Hire for Each
None of these describes one client. They are composites, assembled from problems that turn up again and again with different logos attached. If one reads like your situation, the hire it implies is usually the right call.
The marketing site that grew an application inside it
It started as a fast static site. Then came a pricing configurator, then a customer portal, then a logged-in area. Somewhere in there a modal needed state, the directive went into a shared layout, and over eighteen months almost everything ended up on the client. The site still scores well on the metrics measured at load and badly on the ones measured during interaction. Editors also complain that publishing takes an unpredictable amount of time to show up.
What is usually true underneath: the boundary is in the wrong place and the caching strategy was never designed, only accumulated. The work is unglamorous and highly measurable. Push the boundary down component by component, take the expensive data access back to the server, put the interactive pieces behind their own boundaries, then decide per route what freshness the business actually needs and wire invalidation to the publish event. The hire is a mid to senior engineer with real App Router experience, and the engagement has a number attached at both ends: field interaction latency and client bundle size before, and the same two after.
The migration that stalled at forty per cent
Half the routes are on the App Router, half are not. Two layout systems are live at once. Auth is implemented twice and the versions disagree about one edge case nobody has written down. The team stopped because every remaining route is one of the hard ones, and each attempt turned into a week.
This is not a rewrite and treating it as one is how it stalls again. It is a sequencing problem: order the remaining routes by risk and by how often they change, take the ones already scheduled for feature work first so the migration rides along with something that was being paid for anyway, unify the auth story before anything else because it is the shared dependency, and delete the old route the same week you ship the new one so the two versions never live together long. The hire is somebody who has finished one of these, and the interview question that finds them is which route they would take first and why. If the answer is the hardest one, they are being brave rather than experienced.
The compliance deadline that moves you off the platform
A customer contract or an internal policy requires the application to run inside your own cloud account. The build works locally on the first afternoon and everyone relaxes. Then regeneration behaves differently on each replica, the image endpoint saturates a container during a campaign, a middleware dependency will not build for the runtime, and preview environments, which the whole team had quietly built their review process around, no longer exist.
This is an infrastructure engagement with a Next.js specialist attached, not a frontend ticket. The order of work is a shared cache handler first because it is the one that produces wrong content rather than slow content, then the image strategy, then the runtime question for middleware, then cache headers at the edge, then whatever replaces preview deployments in your workflow. Budget for the review workflow specifically. It is the piece that gets left out and the one the team notices daily.
Working With Next.js Engineers in India: Overlap, Deploys and Cache Incidents
The timezone question deserves arithmetic instead of reassurance, and Next.js adds a second question about deploys that most offshore pages never mention.
The overlap window, in UTC
Convert both working days to UTC and the argument ends. Indian Standard Time is a fixed offset of five hours and thirty minutes, and the country does not operate daylight saving, so a figure worked out in December still holds in June. An engineer at a desk in Mumbai from 09:30 to 18:30 is reachable between 04:00 and 13:00 UTC. Everything in the next three paragraphs is that single pair of numbers, and you can check it yourself in a minute.
Where it is easy. A British working day beginning at 09:00 is 09:00 UTC through the winter and 08:00 UTC once the clocks go forward. Either way it runs into the Indian day until India stops at 13:00 UTC, which gives four shared hours in winter and five in summer, all of it your morning. UK product teams tend to find this arrangement unremarkable, and the reason is arithmetic rather than anything cultural.
Where it is thin. Eastern Australia on standard time is working 23:00 to 07:00 UTC, and only the last three of those hours reach into the Indian morning. Once daylight saving arrives it becomes two. New Zealand has less again. Three hours is enough for a daily conversation and not enough for a working session, so plan the week around that rather than against it.
Where it is nothing. Nine in the morning on the American east coast is 14:00 UTC through the winter, a full hour after the Indian day has already closed, and 13:00 UTC in summer, which is the minute it closes. On the west coast the daylight between the two is four hours wide. There is no window to protect because there is no window, and any supplier suggesting otherwise has not converted the numbers.
Overlap with either American coast therefore has to be manufactured by pushing the Indian day later, and the bill for that lands on somebody's evening. Three live hours with New York in winter means an Indian day finishing around 22:30 local. Two hours with San Francisco means one finishing nearer 00:30. That second one is a real imposition: it shrinks the number of engineers who will accept the role and it shortens how long they stay willing. We would rather say so while you are still writing the brief than let you find out in month two, which is why the hours get agreed with the engineer, in writing, before an engagement begins.
The deploy question this framework adds
Here is the part specific to Next.js. Your riskiest moments are not code changes, they are cache changes: a revalidation strategy adjusted, a route flipped between static and dynamic, an invalidation webhook rewired. Those changes can look perfect in review and be wrong for real traffic, and the symptom is content, so the people who notice are your marketing or content team rather than your monitoring.
If a change like that ships at 17:00 New York time, it lands at 03:30 in India. Nobody is awake to see it. So the working agreement needs three things settled before the first sprint: which deploys are allowed inside the Indian day only, who holds the rollback for anything outside it, and what alert fires when a route that should be cached starts recomputing. That is a fifteen minute conversation at the start of an engagement and an ugly week if you skip it.
Written-first, and what a pull request should contain
With a partial overlap, the artefacts carry the work. For this framework a pull request description that is only "adds the pricing page" is not enough. What reviewers actually need is which routes changed rendering strategy, what the revalidation policy for each of them now is, whether anything crossed the client boundary, and what the build output says about static and dynamic routes before and after. That is four lines in a template, and it converts the most common category of Next.js regression into something a reviewer can catch while the author sleeps.
Written standups beat spoken ones in this shape, and a short decision log is worth more than it costs. Six months on, why a route was made dynamic matters far more than the commit that did it.
Quality control, access and ownership
Quality control is not something a supplier hands you. It is something your own process already does, and it survives the distance for exactly the reason it works in an office. Code lands in a repository you own. The branch rules are yours. The bar for merging is the one your team already applies, and the engineer in Mumbai collects the same review comments anyone else would collect. What deserves a flat refusal, and it is still proposed, is any arrangement where the work happens somewhere you cannot watch it and arrives finished.
Some gates travel across a timezone gap better than others. Type checking and linting belong in the pipeline so that review time goes on design rather than on formatting. A client bundle budget should fail a build outright, because a warning at the bottom of a log is a warning nobody reads. A check on the routes that earn money catches a rendering regression by machine at 04:00 instead of by a person at 09:00. English gets assessed in writing during our interviews, not only on a call, since the pull request description is where a misunderstanding actually costs you a day.
Who owns the code, who owns whatever gets invented along the way, what may be disclosed and how data is handled all belong in the agreement, signed before the first commit. Do not accept anyone's summary of those clauses, this page very much included. Put them in front of your own lawyer. If the application touches personal data of people in the EU or the UK, the storage and transfer questions belong with your legal advisers too, and you are entitled to a straight answer from us about where data physically sits and who can reach it before a repository invitation goes out.
Access control is unglamorous and it is most of the real risk. Individual accounts, never shared ones. Rights limited to the part of the system the work touches. No production credentials given to somebody building pages. A written step for revoking all of it when the engagement ends. We claim no security certifications here, for the company or for individual engineers. If your procurement pack asks for evidence against a named standard, send the question to us in writing and we will answer it with a yes or a no rather than a paragraph.
What If the Developer You Pick Turns Out Not to Be Right?
Every buyer has this question and most offshore pages answer it with adjectives. The practical answer has two halves. Tell us early, and a replacement follows within 48 hours. Then, when you choose that replacement, you are picking from a pool rather than accepting whichever single name arrives, which matters more than it reads: fit here is partly technical and partly whether somebody writes clearly enough to be useful when most of your day and most of theirs never touch.
Catch the mismatch in week two, not month three
The mechanism that saves you the most is not a contractual clause, it is a small first deliverable. Agree something shippable inside the first fortnight, however unglamorous: one route migrated, one caching policy documented and implemented, one measurable improvement on a page you care about. Both sides then learn whether the working model functions while it is still a conversation.
Watch the right signals during that period. Does the pull request description tell you what changed about rendering, or only what changed in the markup. When they hit something ambiguous at 15:00 their time, do they ask in a way that survives your being asleep, or do they guess. Do they read the build output. Those tell you more in two weeks than a technical interview tells you in two hours.
Protecting what the person knows
Whoever holds the model of why your routes are cached the way they are is holding something valuable and undocumented. Insist on written decisions from the start, keep a short record of why each route uses the strategy it does, and pair on anything only one person understands. Do that and a change of engineer is an inconvenience. Skip it and it is a project.
Where Next.js Hires Go Wrong
Writing the brief as a React brief. If the advert lists hooks, state management and component design, you will attract React developers and interview them on React, and the server-side gap will surface in month two. Say which router you are on, whether you self-host, what your caching pain currently is, and whether the person will be expected to touch infrastructure.
Hiring for the framework and forgetting the platform. The single largest variance between two equally strong Next.js engineers is whether they have run the thing outside a managed platform. If you self-host or plan to, that is not a nice-to-have on the scorecard, it is the scorecard.
Screening with a fresh project. A take-home that starts from an empty install tests nothing that matters here, because a new project has no legacy boundary in the wrong place and no cache to be stale. Give them a small, real, awkward piece of your codebase and a timebox, pay for it, and spend the conversation afterwards on trade-offs rather than on the code.
Believing version fluency is the same as version experience. This framework moves quickly and its defaults have genuinely changed between majors, which means an honest candidate will sometimes say they would check the documentation. Reward that. Punishing it selects for people who state outdated defaults with confidence, which is the worse failure by a distance.
No owner for the caching policy. When nobody owns it, every developer makes a local decision, and in a year the site is a patchwork nobody can reason about. One person should be able to answer, for any route, what its freshness policy is and what invalidates it.
Letting the first month produce nothing visible. Onboarding onto a mature Next.js codebase is real work and a few weeks of reduced pace is normal, not a warning sign. What is a warning sign is a first month with nothing shipped and no measurement, because that is how a mismatch stays hidden until it is expensive.
The Next.js Stack We Work In Day to Day
Point at the ones your project genuinely runs on, name the router, and say where it deploys. Those three answers narrow a shortlist faster than any job title.
How to Hire Next.js Developers in India With Us
Go and recruit a Next.js engineer in India yourself and it is the calendar that hurts, not the shortlist. Sourcing and interviewing are the quick part. Then comes the offer, and then the weeks the candidate owes to the employer they are leaving. That delay is built into the market rather than bad luck, which is how a decision taken in one quarter turns into a first day in the following one. Because the engineers are already here when your brief lands, that stretch disappears: a shortlist comes back inside 48 hours, and whoever you pick can be committing to your codebase within 7 days.
Describe the application, not the role
Which router, where it deploys, what your CMS is, whether caching is currently a problem, and what the last incident was. Four sentences of that beat two pages of job specification.
We come back with a shortlist
Profiles matched against those specifics, including what each person has not done. A gap you find in week one is far cheaper than one you find in month three, so we tell you about them.
You run your own technical interview
Run the four questions above, or run whatever you already trust. Would rather watch somebody work through a genuine ticket? Do that instead. Nobody from our side sits in the call or stands between you and the engineer.
Settle the working model before day one
Overlap hours, who owns a deploy outside the Indian day, review standard, and what ships in the first fortnight. Agreeing that in advance turns the timezone gap into a schedule.
Ways to Structure a Next.js Engagement
A piece of work with an edge
A caching strategy designed and implemented across the routes that matter. A client boundary audit with a bundle number before and after. A move off a managed platform into your own cloud. Scoped work with a visible finish line.
A dedicated Next.js engineer
One engineer working inside your repository, your board and your review standard, on the hours settled up front. Most continuing product work ends up in this shape, and it is what the phrase dedicated Next.js developer usually turns out to mean.
Frontend plus platform
A Next.js lead who owns rendering and caching decisions, working with a platform engineer who owns the hosting, the cache store and the pipeline. The right shape when self-hosting is part of the problem rather than a detail.
How the contract is shaped, which hours get worked and what the commercial arrangement looks like are all written down and signed off before a day is billed. Those numbers stay off the page deliberately, because anything quoted without knowing your router, your hosting and your release cadence is not a price, it is a hopeful guess. Where this framework is only one piece of a wider need, the broader view lives on our page about hiring developers in India, and that conversation starts from the product rather than from a job title.
Frequently Asked Questions
Is hiring a Next.js developer the same as hiring a React developer?
No, although the overlap is large. React is a rendering library and its hard problems sit in the browser: state ownership, re-renders, effects. Next.js adds a server runtime, a routing convention, several caching layers and a deployment story. Someone can be excellent at React components and still ship a page that re-renders on the server for every visitor because they never read the build output.
What is the single biggest mistake React developers make in Next.js?
Putting the use client directive too high in the tree. It is not a per-component switch. Everything imported below that module goes into the browser bundle, so one dropdown near the top of a layout can drag an entire page onto the client. The tell in code review is a component that stopped being async because somebody needed a click handler.
Why does our Next.js page still show old content after we published?
Almost always a cache layer, and the useful work is finding out which one. Check whether the route was rendered statically at build time, whether the fetch that supplied the data was cached, whether a revalidation tag was actually triggered by the publish, and whether the browser is reading a soft navigation out of the client router cache. On self-hosted deployments, also check that the invalidation reached every running instance.
Should we build on the App Router or stay on the Pages Router?
New work belongs on the App Router, because Server Components, streaming and the current data model live there and that is where the framework is being developed. Existing Pages Router code does not need an emergency rewrite. Both routers run inside one project, so routes can move across one at a time as they get touched anyway. Ask a candidate which direction they have actually done.
Can we run Next.js on our own infrastructure rather than on Vercel?
Yes. A Node process, or a container built from the standalone output, covers most cases. What you take on is the work the platform was doing quietly: a shared cache store so incremental regeneration works across instances, image resizing through sharp or an external loader, the middleware runtime, and correct cache headers at your own CDN. Scope that at the start rather than at launch.
Are Server Actions safe to expose to the public internet?
They are public HTTP endpoints, so treat them as such. The framework hides the URL, which is not the same as protecting it. Every action needs its arguments validated and the session checked inside the action body. Hiding a delete button in the interface is not authorisation. Ask a candidate where the permission check lives and whether they would trust a record id sent from the browser.
What overlap can we expect with a Next.js engineer working from India?
Work it out in UTC. India holds a fixed offset of five hours and thirty minutes and never moves its clocks, so a Mumbai desk occupied from 09:30 to 18:30 is reachable from 04:00 until 13:00 UTC. Britain shares four of those hours in winter and five in summer. Eastern Australia shares two or three depending on the season. Neither American coast shares any, unless the Indian day is deliberately pushed later by agreement.
How long before a Next.js hire is genuinely productive on our codebase?
On a mature codebase, expect a few weeks of reduced pace and treat that as normal rather than as a warning sign. What shortens it is written context: which routes use which rendering strategy, what invalidates each cache, and where the client boundary is meant to sit. Agree one small shippable thing inside the first fortnight so both sides learn early whether the working model functions.