# Što ćete izgraditi#
Ovaj vodič je praktična kontrolna lista za onboarding Next.js SaaS aplikacije za produkcijski spreman tok u Next.js App Routeru. Fokusira se na ono što se u stvarnim proizvodima najčešće lomi: računi, organizacije, uloge, pozivnice, transakcijski emailovi i konverzija iz probnog u plaćeni plan.
Ako želite dublji kontekst o povezanoj arhitekturi i detaljima naplate, ovi tekstovi nadopunjuju ovu kontrolnu listu:
Zašto onboarding zaslužuje inženjersko vrijeme#
Mala poboljšanja se zbrajaju. Industrijski benchmarkovi tipično pokazuju da 20 do 40 posto prijava u B2B SaaS-u nikad ne dođe do aktivacijskog događaja, a trenje u onboardingu je jedan od glavnih razloga. Tretirajte onboarding kao ključnu površinu proizvoda: mjerite ga, testirajte i učinite robusnim.
# Mapa onboarding toka i metrike uspjeha#
Prije implementacije definirajte korake lijevka i što znači “gotovo”. Uobičajeni B2B SaaS onboarding lijevak:
- 1Kreiraj račun
- 2Verificiraj email
- 3Kreiraj ili se pridruži organizaciji
- 4Dovrši postavljanje workspacea
- 5Pozovi članove tima
- 6Ostvari prvi “value” događaj
- 7Započni probni period
- 8Konvertiraj u plaćeno
Pratite nekoliko ključnih metrika po koraku, ne samo ukupne konverzije.
| Korak lijevka | Događaj “uspjeha” | Što pratiti | Ciljano vrijeme |
|---|---|---|---|
| Prijava | user_created | Korišteni provider, uređaj, referrer | Odmah |
| Verifikacija | email_verified | Vrijeme do verifikacije, bounce stopa | Ispod 10 minuta |
| Postavljanje orga | org_created ili org_joined | Odustajanje, vrijeme do završetka | Ispod 2 minute |
| Prva vrijednost | activated | Koja značajka je aktivirala | Ista sesija |
| Start probnog | trial_started | Trajanje probnog, segment | Isti dan |
| Checkout | checkout_started | Plan, cijena, broj seatova | Tijekom probnog |
| Plaćeno | subscription_active | MRR, signali rizika churn-a | Prije kraja probnog |
ℹ️ Napomena: Rano odlučite koji je vaš aktivacijski događaj. Npr. “kreiran prvi projekt” ili “povezana prva integracija” bolje korelira s retencijom nego “posjećen dashboard”.
# Kontrolna lista jezgre podatkovnog modela za Multi-Tenant SaaS#
Vaš onboarding tok ograničen je podatkovnim modelom. Neka bude jednostavan i eksplicitan.
Minimalne tablice i odnosi#
| Entitet | Ključna polja | Napomene |
|---|---|---|
| users | id, email, name, emailVerifiedAt | Auth provider može dio ovoga spremati |
| organizations | id, name, slug, createdByUserId | Slug pomaže za URL-ove i domene pozivnica |
| memberships | userId, orgId, role, status | Unique index na userId + orgId |
| invites | id, orgId, email, role, tokenHash, expiresAt, acceptedAt | Nikad ne spremati raw tokene |
| subscriptions | orgId, stripeCustomerId, stripeSubscriptionId, status, currentPeriodEnd | Tretirati kao server-truth cache |
| entitlements | orgId, featureKey, value, source, updatedAt | Opcionalno, ali moćno za gating |
Preporučeni obrasci#
- Sve je scoped na org: projekti, računi, API ključevi, zapisi potrošnje.
- Status membershipa:
active,invited,removed. Izbjegavajte brisanje redaka jer gubite auditabilnost. - Entitlements umjesto provjera plana: ograničavajte značajke preko
featureKeyi vrijednosti poputseats=5,api_requests=100000.
💡 Savjet: Ako kasnije očekujete enterprise značajke, dodajte
rolekao string enum i implementirajte provjere dozvola kroz centraliziranu policy funkciju. Izbjeći ćete refaktoriranje desetaka UI provjera.
# Autentikacija i kreiranje računa (App Router)#
Odaberite auth stack koji odgovara vašoj brzini isporuke i potrebama usklađenosti. Ključna stvar je kako auth integrirate u onboarding korake.
Odabir auth providera (brza tablica odluke)#
| Zahtjev | Clerk | Supabase Auth | NextAuth |
|---|---|---|---|
| Najbrže do produkcije | Jako | Srednje | Srednje |
| Ugrađene organizacije i pozivnice | Jako | Slabo | Slabo |
| Potpuna kontrola nad DB-om | Srednje | Jako | Jako |
| SSR i ergonomija u App Routeru | Jako | Jako | Srednje |
| Enterprise SSO kasnije | Jako | Srednje | Srednje |
Za dublju usporedbu i postavljanje, koristite: Vodič za Next.js autentikaciju: NextAuth vs Clerk vs Supabase
Kontrolna lista: kreiranje računa#
- Koristite server-side provjere sesije u server komponentama i route handlerima.
- Za B2B proizvode zahtijevajte verifikaciju emaila osim ako nemate vrlo jak razlog da ne.
- Normalizirajte i spremite
emailu lowercase radi jedinstvenosti. - Obradite OAuth račune koji možda nemaju verificiran email ovisno o provideru.
- Kreirajte
userredak pri prvom loginu preko webhooka ili callbacka, ne na klijentu.
App Router guard obrazac#
Koristite server-side guard za onboarding korake kako biste izbjegli “flash pogrešne stranice” i client bypass.
// app/(app)/onboarding/page.tsx
import { redirect } from "next/navigation";
export default async function OnboardingPage() {
const user = await getCurrentUser(); // server-side
if (!user) redirect("/sign-in");
const org = await getActiveOrgForUser(user.id);
if (org) redirect(`/app/${org.slug}`);
return <OnboardingWizard />;
}Ovaj obrazac sprječava korisnike da preskoče onboarding tako da direktno posjete URL-ove.
# Kreiranje organizacije i postavljanje workspacea#
Većina SaaS proizvoda nije zauvijek single-user. Čak i ako krenete s “personal accounts”, B2B kupci će brzo tražiti timove i vlasništvo nad naplatom.
Kontrolna lista: korak kreiranja organizacije#
- Omogućite putanje kreiraj org i pridruži se orgu.
- Osigurajte jedinstven
slugi validirajte pravila imenovanja. - Kreirajte prvi membership kao
owner. - Odlučite može li jedan user pripadati više orgova i podržite prebacivanje orgova.
- Persistirajte
activeOrgIdu cookie ili profilno polje, ali ga validirajte server-side.
Uobičajen UI tok#
- Korak 1: Naziv organizacije i slug
- Korak 2: Opcionalni workspace defaulti (timezone, industrija, predložak)
- Korak 3: Pozovi članove tima
Neka prvi korak bude minimalan. Svako dodatno polje smanjuje konverzije.
⚠️ Upozorenje: Nemojte spremati autorizacijsko stanje poput
role=owneru local storage i tretirati ga kao istinu. Uloge uvijek rješavajte iz baze na serveru.
# Uloge i dozvole (RBAC) koje stvarno možete održavati#
RBAC je lako započeti, a lako i pokvariti kad značajke rastu. Koristite jednostavan skup uloga i centralno derivirajte dozvole.
Preporučene osnovne uloge#
| Uloga | Tipične dozvole | Tko je dobiva |
|---|---|---|
| owner | naplata, upravljanje korisnicima, brisanje orga | Osnivač, billing admin |
| admin | upravljanje postavkama, upravljanje korisnicima, bez brisanja naplate | Voditelj tima |
| member | korištenje značajki proizvoda | Većina korisnika |
| viewer | samo čitanje | Finance, stakeholderi |
Funkcija policyja za dozvole#
Centralizirajte logiku dozvola kako ne biste posvuda imali if role === ....
// lib/authz.ts
export type Role = "owner" | "admin" | "member" | "viewer";
export function can(role: Role, action: string) {
const rules: Record<Role, string[]> = {
owner: ["billing:write", "users:write", "org:delete", "app:use"],
admin: ["users:write", "settings:write", "app:use"],
member: ["app:use"],
viewer: ["app:read"],
};
return rules[role]?.includes(action) ?? false;
}Koristite ovo u route handlerima i server actions za provođenje pristupa.
Kontrolna lista: RBAC implementacija#
- RBAC provodite u server kodu, ne samo u UI-u.
- Neka svaki write endpoint zahtijeva org kontekst.
- Dodajte audit trail za osjetljive akcije: promjene uloga, promjene naplate, brisanja.
- Spriječite ownera da ukloni zadnjeg ownera.
- Logirajte authorization failure s org i user identifikatorima.
# Team pozivnice koje ne pucaju (tokeni, istek, idempotencija)#
Pozivnice su mjesto gdje mnogi SaaS proizvodi propuštaju sigurnost. Tretirajte pozivnice kao linkove za reset lozinke.
Pregled toka pozivnice#
- 1Admin upisuje email i ulogu.
- 2Server kreira invite s
expiresAtitokenHash. - 3Šalje se email koji sadrži raw token u URL-u.
- 4Primatelj klikne link, prijavi se i prihvati pozivnicu.
- 5Server verificira token hash i istek, zatim kreira membership.
Kontrolna lista: sigurna implementacija pozivnica#
- Spremite samo hashirani token, nikad raw token.
- Uključite
expiresAt(često 7 dana). - Accept endpoint učinite idempotentnim: klik dvaput ne smije kreirati duplikate.
- Ako membership već postoji, pretvorite pozivnicu u UX “pridruži se postojećem”.
- Invalidirajte pozivnice nakon prihvaćanja postavljanjem
acceptedAt.
Generiranje tokena i hashiranje#
// lib/invites.ts
import crypto from "crypto";
export function generateInviteToken() {
const token = crypto.randomBytes(32).toString("hex");
const tokenHash = crypto.createHash("sha256").update(token).digest("hex");
return { token, tokenHash };
}Prihvat pozivnice u route handleru#
// app/api/invites/accept/route.ts
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { token } = await req.json();
const user = await getCurrentUser();
if (!user) return NextResponse.json({ error: "unauthorized" }, { status: 401 });
const tokenHash = sha256(token);
const invite = await db.invite.findValidByHash(tokenHash);
if (!invite) return NextResponse.json({ error: "invalid" }, { status: 400 });
await db.membership.upsert({
userId: user.id,
orgId: invite.orgId,
role: invite.role,
});
await db.invite.markAccepted(invite.id);
return NextResponse.json({ ok: true });
}Ako je moguće, DB operacije držite transakcijskima.
💡 Savjet: Dodajte gumb “ponovno pošalji pozivnicu” koji kreira novi token i invalidira stari invite. Ponovno slanje istog tokena povećava rizik curenja linka u shared inboxovima.
# Transakcijski emailovi: verifikacija, pozivnice, trial događaji i računi#
Email je dio onboardinga, ne marketing. Ako korisnici ne primaju poruke, onboarding se lomi.
Preporučeni email stack#
| Potreba | Opcija | Zašto |
|---|---|---|
| Slanje emailova preko API-ja | Resend, Postmark, SendGrid | Pouzdana dostavljivost i webhooks |
| Predlošci | React Email ili MJML | Predlošci u kodu s verzioniranjem |
| Inbound događaji | Provider webhooks | Bounce, complaints, delivery |
Vrste emailova koje biste trebali rano implementirati#
| Okidač | Kritična polja | |
|---|---|---|
| Verificiraj email | sign-up | verification URL, istek |
| Poziv u workspace | invite created | naziv orga, uloga, accept URL, istek |
| Probni period započeo | početak triala | datum završetka triala, sljedeći korak |
| Probni period uskoro završava | 3 dana prije kraja | CTA za upgrade, što se dalje događa |
| Uspješna uplata | invoice paid | link na račun, plan, iznos |
Kontrolna lista: pouzdanost transakcijskih emailova#
- Konfigurirajte SPF, DKIM i DMARC za svoju domenu.
- Koristite dedicated sending domenu poput
mail.yourapp.com. - Kroz provider webhooks označite emailove kao bounced ili complained.
- Ne šaljite onboarding emailove iz client koda.
- Uključite plain-text fallback i izbjegavajte spammy naslove.
# Logika probnog perioda i feature gating u Next.js-u#
Probni periodi postaju kaos kad se “trial” tretira kao UI labela. Implementirajte ga kao stanje plus entitlements.
Model stanja probnog perioda#
Trial možete implementirati u svojoj bazi ili isključivo u Stripeu. U praksi timovi često koriste oboje:
- Stripe pretplata s probnim periodom radi točnosti naplate.
- DB polje radi onboarding UX-a, praćenja i internih overrideova.
| Polje | Primjer | Source of Truth |
|---|---|---|
trialEndsAt | 2026-07-07T00:00:00Z | Stripe, preslikano u DB |
subscriptionStatus | trialing, active, past_due | Stripe |
entitlements | seats=5, exports=true | Derivirano iz Stripea i internih pravila |
Kontrolna lista: feature gating koji preživljava edge caseove#
- Ograničavajte server-side u route handlerima i server actions.
- Koristite entitlements poput
exports=trueumjestoplan=pro. past_duetretirajte kao “ograničen pristup” ili “samo čitanje”, ne instant lockout.- Pažljivo s vremenskim zonama, uvijek spremajte timestampove u UTC.
- Cacheajte entitlements po orgu s kratkim TTL-om, ali omogućite trenutno osvježenje nakon webhook događaja.
⚠️ Upozorenje: Ako trial status provjeravate samo na klijentu, korisnici mogu zaobići ograničenja direktnim pozivima API-ja. Svi paid gateovi moraju se provoditi server-side.
# Konverzija iz probnog u plaćeni plan uz Stripe (Checkout, webhooks i pristup)#
Upgrade tok treba biti bez trenja i determinističan. Najčešći produkcijski incident je “korisnik je platio, ali je i dalje zaključan”, uzrokovan izostankom webhook obrade ili pogrešnim mapiranjem orga.
Za dublju implementaciju naplate, koristite: Next.js Stripe pretplate: SaaS naplata
Kontrolna lista: kreiranje Checkout sessiona#
- Kreirajte Checkout sessione server-side.
- U Stripe metadata uvijek proslijedite
orgId. - Koristite stabilan
customerIdpo orgu kako bi fakture i payment metode bile centralizirane. - Odlučite naplaćujete li po seat-u i uskladite s brojem membershipa.
- Nakon uspjeha redirectajte na stranicu koja poll-a za refresh entitlements.
Primjer Stripe metadata#
// app/api/billing/checkout/route.ts
export async function POST() {
const { orgId } = await requireOrgContext();
const session = await stripe.checkout.sessions.create({
mode: "subscription",
customer: await getOrCreateStripeCustomerId(orgId),
line_items: [{ price: process.env.STRIPE_PRICE_ID!, quantity: 1 }],
success_url: `${process.env.APP_URL}/billing/success`,
cancel_url: `${process.env.APP_URL}/billing`,
metadata: { orgId },
});
return Response.json({ url: session.url });
}Kontrolna lista za webhooks (nepregovorno)#
- Verificirajte Stripe webhook signature.
- Obradite događaje idempotentno koristeći event ID-ove.
- Ažurirajte svoju
subscriptionscache tablicu i ponovno izračunajte entitlements. - Razmotrite
checkout.session.completed,customer.subscription.updated,invoice.paid,invoice.payment_failed. - Nikad se ne oslanjajte samo na success redirect.
Obrazac idempotencije webhooks#
// app/api/stripe/webhook/route.ts
export async function POST(req: Request) {
const body = await req.text();
const sig = req.headers.get("stripe-signature")!;
const event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
const already = await db.webhookEvents.exists(event.id);
if (already) return new Response("ok");
await db.webhookEvents.insert({ id: event.id, type: event.type });
await handleStripeEvent(event);
return new Response("ok");
}Neka handler bude mali i delegirajte po tipu događaja.
# Onboarding UX u App Routeru: koraci, redirecti i server actions#
Želite da onboarding bude gladak, ali i ispravan pod refreshima i korištenjem više tabova.
Preporučeni pristup#
- Spremite onboarding napredak server-side kao
onboardingStatepo useru ili membershipu. - Redirectajte sa servera na temelju trenutnog stanja.
- Koristite server actions za slanje koraka kako biste smanjili API boilerplate.
Jednostavan onboarding state machine#
| Stanje | Značenje | Sljedeći korak |
|---|---|---|
needs_org | nema membership u orgu | kreiraj ili se pridruži orgu |
needs_profile | nedostaju obavezna profilna polja | profil forma |
needs_invites | nije pozvan nijedan teammate | ekran pozivnica |
activated | ostvaren prvi value | glavna aplikacija |
Kontrolna lista: App Router onboarding mehanika#
- Svaka ruta koraka treba biti direktno posjetiva i server-guardana.
- Nakon slanja forme ažurirajte stanje i redirectajte na sljedeći korak.
- Nikad ne računajte “trenutni org” iz client statea bez server-side provjere.
- Držite forme otporne na refresh, retry i mrežne greške.
🎯 Ključna poruka: Onboarding treba biti serverom provedena state mašina, ne client wizard s best-effort redirectima.
# Observabilnost: mjerite odustajanje i debuggajte korisničke prijave#
Onboarding je “gotov” tek kad možete objasniti što se dogodilo za specifičnog usera i org.
Što instrumentirati#
- Funnel događaje: sign-up, verify, org created, invite sent, invite accepted, checkout started, subscription active.
- Latenciju: vrijeme od sign-upa do aktivacije, vrijeme od starta triala do checkouta.
- Error rateove na kritičnim endpointima: accept pozivnice, kreiranje checkouta, obrada webhookova.
Praktična kontrolna lista#
- Dodajte strukturirane logove s
userId,orgId,requestId. - Pratite onboarding događaje product analytics alatom ili vlastitom tablicom.
- Alertajte na Stripe webhook failure i backlog.
- Spremite događaje dostave emaila i bounceove za invite emailove.
# Uobičajene zamke (i kako ih izbjeći)#
Ovi problemi se stalno pojavljuju u produkcijskim Next.js SaaS implementacijama.
Zamka 1: Miješanje user naplate i org naplate#
Ako jedan user pripada više orgova, naplata mora biti u vlasništvu orga ili ne možete čisto prebaciti vlasništvo.
Rješenje: spremite stripeCustomerId i subscription zapise na orgId, ne na userId.
Zamka 2: Slaba sigurnost pozivnica#
Plain tokeni spremljeni u bazi cure kroz logove, backupove i admin dashboarde.
Rješenje: spremite hash tokene, dodajte istek i invalidirajte na resend.
Zamka 3: Autorizacija samo na klijentu#
Ako sakrijete gumbe, ali dopustite API pristup, korisnici će i dalje izvršavati akcije.
Rješenje: provodite provjere dozvola u route handlerima, server actions i svim background jobovima.
Zamka 4: Ne-idempotentni webhookovi#
Stripe ponavlja evente. Ako kreirate više pretplata ili krivo prepišete stanje, pristup će se nasumično “prebacivati”.
Rješenje: spremite event ID-ove i učinite handlere sigurnima za ponovnu obradu.
Zamka 5: Trial gateovi temeljeni na nazivima planova#
Nazivi planova se mijenjaju, promocije se događaju, postoje enterprise overrideovi.
Rješenje: ograničavajte po entitlementima i zadržite jedan resolver za entitlements.
# Preporučene biblioteke i obrasci (2026.)#
Koristite provjerene, održavane alate. Izbjegavajte pisanje vlastitih security-critical primitiva.
| Područje | Preporučene opcije | Napomene |
|---|---|---|
| Auth | Clerk, Supabase Auth, NextAuth | Odaberite prema kontroli vs brzini |
| DB ORM | Prisma, Drizzle | Koristite migracije i unique constraintove |
| Emailovi | Resend, Postmark | Dodajte domain auth i webhooks |
| Plaćanja | Stripe | Webhooks i idempotencija su obavezni |
| Validacija | Zod | Validirajte inpute u server actions |
| Rate limiting | Upstash, Cloudflare | Zaštitite pozivnice i auth endpointove |
| Observabilnost | Sentry, OpenTelemetry | Hvatajte onboarding greške |
Za obrasce strukturiranja multi-tenant aplikacije koji utječu na onboarding i RBAC, pogledajte: Vodič za multitenant SaaS arhitekturu u Next.js-u
# Ključne poruke#
- Implementirajte onboarding kao serverom provedenu state mašinu sa server-side redirectima u App Routeru.
- Modelirajte multi-tenancy eksplicitno s organizations, memberships i invites, i uvijek scopeajte writeove na org.
- Osigurajte team pozivnice s hashiranim tokenima, istekom i idempotentnim prihvatom, te logirajte svaku promjenu membershipa.
- Ograničavajte plaćene značajke preko entitlements i provodite pristup server-side u route handlerima i server actions.
- Učinite trial-to-paid pouzdanim uz Stripe metadata mapiranje na orgId i idempotentnu obradu webhookova.
# Zaključak#
Produkcijski spreman onboarding tok u Next.js-u nije samo sign-up forma. To je koordiniran sustav identiteta, org konteksta, dozvola, isporuke emaila i billing stanja koji mora ostati ispravan kroz retry, refresh i edge caseove.
Ako želite da Samioda implementira ili auditira vaš onboarding end-to-end, uključujući App Router guardove, org RBAC, transakcijske emailove i Stripe konverziju iz probnog u plaćeni plan, kontaktirajte nas i mi ćemo mapirati vaš funnel, isporučiti kontrolnu listu i instrumentirati metrike koje pomiču aktivaciju i prihod.
FAQ
Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.
Više iz kategorije Web razvoj
Sve →React Query u velikim aplikacijama: invalidacija cachea, paginacija i obrasci mutacija za stvarne aplikacije
Najbolje prakse invalidacije cachea u React Queryju za aplikacije iz stvarnog svijeta: skalabilan dizajn query keyjeva, strategija invalidacije, optimistična ažuriranja, infinite queryji i pozadinski refetch u Next.js App Routeru.
React performanse u 2026.: profiliranje, memoizacija i obrasci renderiranja koji stvarno rade
Praktičan vodič korak po korak za profiliranje performansi i memoizaciju u Reactu u 2026.: kako dijagnosticirati spora sučelja uz React DevTools Profiler i why-did-you-render, odabrati prave obrasce renderiranja i izbjeći preuranjenu optimizaciju.
Next.js Edge Runtime vs Node.js Runtime (Vercel i Cloudflare): Što pokretati gdje
Praktičan okvir za odlučivanje između Next.js Edge Runtime i Node.js Runtime u 2026., s konkretnim primjerima, ograničenjima i završnom matricom use-caseova.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Next.js + Supabase SaaS početna arhitektura (App Router): Auth, RLS, naplata i multi-tenancy
Production-ready nacrt za Next.js App Router + Supabase SaaS početnu arhitekturu: autentikacija, Postgres podatkovni model, RLS politike, Stripe naplata i multi-tenant dizajn organizacija s konkretnim primjerima.
Next.js + Supabase RLS za multi‑tenant SaaS: pravila, uloge i siguran pristup podacima
Praktičan vodič za Next.js App Router i Supabase Row Level Security za multi-tenant SaaS: dizajn tablica, pravila, uloge, obrasci pristupa na serveru, česte zamke i kontrolna lista za deployment.
Implementacija Stripe pretplata u Next.js-u: Billing Portal, webhookovi i prava pristupa
Vodič spreman za produkciju za Next.js Stripe pretplate: planovi i probni periodi, Billing Portal, provjera webhookova, idempotentna obrada, mapiranje prava pristupa i testiranje sa Stripe CLI-jem.