Tech Stack
| Category | Technology |
|---|---|
| Framework | Next.js 16 (App Router), React 19, TypeScript |
| Database | Supabase (PostgreSQL + Auth + Storage + RLS) |
| Auth | Supabase Auth — Magic Link + OTP code fallback (length configurable 6–10 via OTP_LENGTH, default 6; sent via the active email provider) with cross-tab auto-redirect; OAuth providers from a config-driven catalog. |
| Payments | Stripe (Subscriptions + One-time) |
| AI | LangChain + Multi-LLM (OpenAI, Anthropic, Google) |
Provider-agnostic abstraction — Brevo or Mailjet (transactional + newsletter), pluggable via EMAIL_PROVIDER |
|
| Styling | Tailwind CSS 4, shadcn/ui (New York style) |
| i18n | URL-based routing (/fr-FR/..., /en-US/..., /en-CA/..., /fr-CH/...) |
| Security | CSRF, rate limiting (Upstash Redis), CSP headers, recent-auth gates for destructive admin actions |
| Bot Protection | Cloudflare Turnstile (invisible CAPTCHA) |
| Analytics | Google Tag Manager, Google Ads, Meta Pixel, X Pixel (all consent-gated) |
| Live Chat | Crisp (consent-gated user identification) |
| Caching | Upstash Redis (rate limiting, sessions) |
| Background Jobs | Internal scheduler + Supabase Edge Functions |
Third-Party Integrations
The boilerplate includes pre-configured integrations for common SaaS needs:
Stripe
Stripe powers all billing: subscriptions (Free, Pro, Business plans), one-time credit pack purchases, and license payments. It includes Checkout Sessions for payment collection, Customer Portal for self-service subscription management, and a comprehensive webhook flow for lifecycle events (subscription changes, invoice payments, refunds, disputes). The route at app/api/stripe/webhook/route.ts verifies the signature and delegates to core/billing/stripe-webhook.ts; side effects are split across focused modules such as core/billing/mutations.ts, core/billing/customer-events.ts, core/billing/risk-events.ts, and core/billing/free-plan.ts. Plans are defined in config/pricing.ts and matched by Stripe Price ID. Promotion/coupon codes can be enabled at checkout via the allowPromotionCodes flag in the pricing config.
Supabase
Supabase provides the entire backend layer: PostgreSQL database with Row Level Security (RLS), authentication (magic links + OAuth), file storage for media uploads, and Edge Functions for background job execution. Three client types are available: createClient() for user-scoped server requests, createServiceClient() to bypass RLS for admin operations, and supabaseAdmin as a pre-instantiated admin client.
Adding your own account-scoped table
Every domain table carries account_id (never a bare user_id) and is filtered by membership. Replicate this pattern for any new core/app table, and mirror the change into supabase/schema.sql in the same commit. CMS/blog table changes mirror into supabase/cms-schema.sql. Migrations alone are not enough because fresh resets use the relevant full-schema file:
alter table my_table enable row level security;
create policy "members_read" on my_table for select
using (exists (
select 1 from memberships m
where m.account_id = my_table.account_id
and m.user_id = auth.uid()
));
-- INSERT/UPDATE/DELETE must be at least as strict as SELECT
create policy "members_write" on my_table for all
using (exists (
select 1 from memberships m
where m.account_id = my_table.account_id
and m.user_id = auth.uid()
));If a policy needs to query memberships recursively, use a SECURITY DEFINER helper (e.g. user_belongs_to_account()) instead of an inline sub-select to avoid RLS recursion.
AI / Multi-LLM
AI is orchestrated via LangChain with support for multiple LLM providers: OpenAI (GPT-4.1, GPT-4o, O3, O4-mini), Anthropic (Claude Opus 4.5, Sonnet 4.5, Haiku 4.5), and Google (Gemini 3 Pro, 2.5 Pro/Flash, 2.0 Flash). A multi-agent architecture with dynamic registry allows built-in agents (chat, code-assistant, translator, writer, rag) and custom agents. Includes prompt caching utilities (prepared per provider), token usage tracking with cost calculation, and streaming responses via SSE.
Cloudflare Turnstile
Turnstile provides invisible bot protection without annoying CAPTCHAs. It's used on contact forms, login, and other sensitive endpoints.
Google Tag Manager, Google Ads, Meta Pixel & X Pixel
Analytics are consent-aware and privacy-by-default. GTM, gtag.js / Google Ads, Meta Pixel, X (Twitter) Pixel, and purchase/signup conversion events only load or fire after the user grants the matching analytics or marketing consent. Consent Mode v2 still starts deny-by-default, but third-party scripts are not mounted before consent. To enable Google Tag Manager, set NEXT_PUBLIC_GTM_ID. For Google Ads conversion tracking, set NEXT_PUBLIC_GOOGLE_ADS_ID and NEXT_PUBLIC_GOOGLE_ADS_CONVERSION_LABEL. Optionally set NEXT_PUBLIC_GA_MEASUREMENT_ID for GA4 via gtag.js (skip if GA4 is already configured in your GTM container to avoid duplicate events). For Meta Pixel, set NEXT_PUBLIC_META_PIXEL_ID. For the X Pixel, set NEXT_PUBLIC_X_PIXEL_ID plus the optional per-action conversion event IDs NEXT_PUBLIC_X_PIXEL_EVENT_PURCHASE / NEXT_PUBLIC_X_PIXEL_EVENT_SIGNUP. A unified trackConversion() method fires purchase events only when marketing consent is granted.
Crisp Live Chat
Crisp provides live chat with automatic user identification for logged-in users. Set the NEXT_PUBLIC_CRISP_WEBSITE_ID variable to enable it. The script and user-identification payload are gated by consent, so Crisp is not loaded before the user grants the configured support/marketing consent category.
Email (Brevo / Mailjet)
Transactional emails (magic links, workspace invitations, contact-form confirmations, license expiration warnings, organization deletion notices) and newsletter management go through a provider-agnostic abstraction in lib/email/. Switching providers is a config-only change — set EMAIL_PROVIDER and supply the matching credentials.
Supported providers:
brevo— Brevo (formerly Sendinblue). Single API key (BREVO_API_KEY) +BREVO_NEWSLETTER_LIST_ID.mailjet— Mailjet Send API v3.1 + Contacts. Two keys (MAILJET_API_KEY_PUBLIC+MAILJET_API_KEY_PRIVATE) + optionalMAILJET_NEWSLETTER_LIST_ID.noop— logs and reports success without delivering. Useful for dev / CI.
The shared sender identity (EMAIL_FROM_NAME, EMAIL_FROM_ADDRESS) and inbox addresses (CONTACT_EMAIL, SUPPORT_EMAIL) apply to every provider. Email templates are bilingual (FR / EN) and live in lib/email/templates.ts; lib/email/translations.ts holds the strings. Failed transactional sends are queued in the pending_emails table and retried with exponential backoff by the process-pending-emails job.
Adding a new provider is a 3-step change: implement the EmailProvider interface under lib/email/providers/<name>.ts, add a credentials slice in config/email.ts, and add a branch in lib/email/provider.ts. No call sites need to change.
Upstash Redis
Upstash Redis provides distributed rate limiting across all API endpoints. Set UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN to enable it. Falls back to in-memory rate limiting when not configured, which works for single-instance deployments but won't share state across multiple instances.
Serwist (PWA)
Serwist powers the Progressive Web App layer with service worker generation, offline support, and push notifications. It provides automatic precaching of static assets, stale-while-revalidate for resources, network-first for API calls, and an offline fallback page. Push notifications use the Web Push API with VAPID authentication, per-device subscriptions stored in the database, and user notification preferences. Configured via the withSerwist wrapper in next.config.ts.
Account-Centric Model
Everything is attached to an Account, never directly to a User. This enables multi-tenant architecture with clear separation of resources.