Django and Next.js: Drawing the Line Between Them
I've shipped this pairing on client work and my own products for years now. Django handles data and business rules, Next.js handles the interface. It's a good split.
The friction is never in the setup. It's in the hundred small decisions afterward about which side owns what.
The default: Django owns truth, Next owns presentation
The rule I keep coming back to: if getting it wrong corrupts data or costs money, it lives in Django.
That covers more than it first appears. Pricing. Permissions. Anything that writes to more than one table. Anything that touches a payment provider. Anything whose result you'd need to audit later.
Next.js owns what the user sees and how they move through it — routing, layout, optimistic updates, form state, the loading and error surfaces.
Validation lives in both places, deliberately
This is the one that trips people up, because it looks like duplication and isn't.
The frontend validates for feedback: catching an empty field before a round trip, showing an inline error as someone types. The backend validates for correctness: nothing invalid reaches the database, regardless of what the client sent.
You need both. Frontend-only validation is bypassed by anyone with a terminal. Backend-only validation gives you a form that feels broken. They serve different purposes and should be written independently rather than shared through some clever mechanism.
What you shouldn't duplicate is business logic. Validation rules, yes. The rule about which discount applies to which customer tier, no — that has exactly one home.
Where I've gotten it wrong
Putting derived state in the frontend. Computing an order total in React because it was convenient for display, then discovering the backend calculated it slightly differently. Two sources of truth for a number the customer pays. Compute it server-side and send the result.
Letting the API mirror the UI. Building endpoints shaped exactly like a screen — /api/dashboard-page-data. It feels efficient right up until a second screen needs some of it, and now you've got endpoints that can't be recombined. Shape endpoints around resources; let the frontend compose.
Being clever about auth boundaries. Every "this route is basically public, we can skip the check" turns into an incident eventually. Check on the server, every time, including for routes that feel harmless.
The practical setup
Django REST Framework for the API. Token or session auth depending on whether it's a mobile client. Next.js App Router with Server Components fetching directly where it can, client components only where interactivity demands it.
The thing worth investing in early is a shared type definition — generated from your serializers if you can manage it. The gap between what Django returns and what TypeScript believes it returns is where a surprising number of production bugs live.
When not to split
If it's a straightforward CRUD app with no mobile client and no third-party API consumers, the split may be costing you more than it returns. Two deploy targets, two sets of dependencies, a network hop between layers that used to be a function call.
Django with server-rendered templates is still an excellent answer for a lot of products. Reach for the split when you actually have a second consumer of that API, or when the interface is genuinely interactive enough to need a real frontend framework — not because it's the modern-looking choice.