Next.js projekt koji krene kao jednostavna aplikacija s desetak stranica lako preraste u kaos kad broj ruta, komponenti i API endpointa naraste na stotine. Dobra struktura ne sprječava rast projekta, ali sprječava da taj rast postane bolan. Evo pristupa koji se pokazao pouzdanim za veće Next.js projekte s App Routerom.

Osnovno pravilo: grupiraj po značajki, ne po tipu datoteke

Najčešća greška je struktura gdje su svi komponenti u /components, sve funkcije u /lib, svi hookovi u /hooks — bez obzira na to čemu služe. Za male projekte to je u redu, ali za veće, bolje je grupirati kod prema domeni ili značajki:

src/
  features/
    auth/
      components/
      hooks/
      actions.ts
      types.ts
    billing/
      components/
      hooks/
      actions.ts
      types.ts
  app/
    (routes samo, minimalna logika)
  components/
    ui/          ← generičke, ponovno iskoristive komponente (button, input...)
  lib/
    (utility funkcije bez poslovne logike)

Ovaj pristup znači da kad radite na naplati (billing), sav relevantan kod je u jednom folderu — ne morate skakati između pet različitih direktorija da nađete sve dijelove jedne funkcionalnosti.

App Router: route grupe za organizaciju bez utjecaja na URL

Next.js App Router podržava route grupe — foldere u zagradama koji ne utječu na konačni URL, ali vam dopuštaju logičko grupiranje ruta i različite layoute za različite dijelove aplikacije:

app/
  (marketing)/
    layout.tsx      ← javni layout, header/footer za marketing stranice
    page.tsx         → "/"
    o-nama/page.tsx  → "/o-nama"
  (dashboard)/
    layout.tsx       ← autentificiran layout sa sidebarom
    postavke/page.tsx → "/postavke"

Dvije potpuno različite navigacije, dva različita layouta, a URL strukture ostaju čiste.

Server komponente kao default, client komponente kao iznimka

U velikom projektu, disciplina oko "use client" direktive je bitna za performanse. Praktično pravilo: klijentska komponenta treba biti što manja i što niže u stablu komponenti — izvucite interaktivni dio (npr. gumb s onClick) u zaseban mali file, a sve oko njega ostavite kao server komponentu.

// ProizvodKartica.tsx — server komponenta (default)
import DodajUKosaricuGumb from "./DodajUKosaricuGumb";

export default function ProizvodKartica({ proizvod }) {
  return (
    <div>
      <h3>{proizvod.naziv}</h3>
      <p>{proizvod.cijena} €</p>
      <DodajUKosaricuGumb id={proizvod.id} />
    </div>
  );
}
// DodajUKosaricuGumb.tsx — jedina client komponenta
"use client";
export default function DodajUKosaricuGumb({ id }: { id: string }) {
  return <button onClick={() => dodajUKosaricu(id)}>Dodaj u košaricu</button>;
}

Server Actions umjesto raštrkanih API ruta

Za formu ili mutaciju podataka koja se koristi samo unutar aplikacije, Server Actions često zamjenjuju potrebu za zasebnim /api endpointom, čime se smanjuje broj datoteka i duplicirane validacijske logike:

// features/billing/actions.ts
"use server";

export async function azurirajPretplatu(formData: FormData) {
  const plan = formData.get("plan");
  // validacija, poziv bazi, revalidatePath...
}

API rute (app/api/.../route.ts) rezervirajte za slučajeve kad vam stvarno treba HTTP endpoint — webhookove, integracije s vanjskim servisima ili javni API.

Dijeljeni tipovi i validacija na jednom mjestu

U velikim projektima česta bol je isti tip podataka definiran na tri različita mjesta (baza, backend, frontend). Definirajte Zod sheme jednom, izvedite TypeScript tipove iz njih:

// features/billing/types.ts
import { z } from "zod";

export const PretplataSchema = z.object({
  plan: z.enum(["basic", "pro", "enterprise"]),
  cijena: z.number().positive(),
});

export type Pretplata = z.infer<typeof PretplataSchema>;

Praktični checklist za veći projekt

  • Poslovna logika grupirana po značajki, ne po tipu datoteke
  • Route grupe koriste se za odvajanje layouta (javno vs. autentificirano)
  • Client komponente su male i izolirane, ne obavijaju cijele stranice
  • Server Actions za interne mutacije, API rute samo kad su stvarno potrebne
  • Zod sheme kao jedini izvor istine za oblik podataka

Zaključak

Struktura koja radi za 10 stranica ne radi nužno za 500. Ključna promjena mentaliteta je prijelaz s organizacije "po tipu datoteke" na organizaciju "po značajki", uz disciplinu oko toga gdje živi client-side kod. Ta dva poteza sama po sebi rješavaju veći dio boli koja dolazi s rastom Next.js projekta.