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)
Email 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 - Subscriptions, one-time payments & billing
Supabase - Database, Auth, Storage & Edge Functions
LangChain - Multi-LLM AI orchestration
Email (Brevo / Mailjet) - Transactional emails & newsletter via pluggable provider
Cloudflare Turnstile - Bot protection for forms
Google Tag Manager - Analytics & tracking
Google Ads - Per-action conversion tracking (purchase & signup) via gtag.js
Meta Pixel - Facebook/Instagram ads tracking
Crisp - Live chat widget
Upstash Redis - Rate limiting & caching
Serwist - PWA, offline support & push notifications

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) + optional MAILJET_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.

USER (auth.users) │ │ can have multiple ▼ MEMBERSHIPS (user_id, account_id, role_slug) │ │ belongs to ▼ ACCOUNT (personal | workspace) ├── Credits Balance ├── Subscription (Stripe) ├── AI Usage Logs ├── Chat Sessions ├── API Keys (B2B) └── Settings