Skip to main content

Best SaaS Boilerplates for GDPR Compliance 2026

·StarterPick Team
Share:

TL;DR

GDPR compliance isn't a plugin — it's an architectural choice. The decisions baked into your boilerplate (how auth is stored, where data lives, whether you can delete a user completely) determine how hard GDPR compliance will be. The best boilerplates for GDPR in 2026 are: Supastarter (Supabase RLS ensures data isolation; EU data center option), T3 Stack (you control every data store), and self-hosted Supabase + any boilerplate (data never leaves your servers). Here's what GDPR actually requires technically and how each boilerplate stacks up.

Key Takeaways

  • GDPR core requirements: right to deletion, right to access, data portability, consent management, breach notification
  • Boilerplate impact: data model (org isolation, cascade deletes) and hosting choice (EU servers)
  • Supastarter: best pre-built RLS isolation + EU Supabase region support
  • T3 Stack + self-hosted: maximum control, minimum third-party data sharing
  • The hard GDPR parts: deleting user data from all third-party integrations (analytics, CRM, email)
  • Cookie consent: all boilerplates need a consent manager added — none ship with one

What GDPR Actually Requires (Technically)

GDPR is a legal framework, but its requirements translate into concrete technical features:

GDPR RequirementTechnical Implementation
Right to erasure (Art. 17)CASCADE DELETE from all tables; purge from analytics, email, CRM
Right to access (Art. 15)Export all user data as JSON/CSV
Data portability (Art. 20)Machine-readable data export
Consent (Art. 7)Cookie consent manager; opt-in not pre-ticked
Data minimization (Art. 5)Don't store data you don't need
Breach notification (Art. 33)Incident response process, 72-hour notification
Data Processing AgreementsDPAs with all vendors (Stripe, SendGrid, etc.)
Privacy by design (Art. 25)Default settings protect privacy
EU data residencyOptional but often contractually required

The Four GDPR-Critical Technical Features

1. Complete User Data Deletion

The most technically challenging GDPR requirement is "right to erasure" — you must be able to delete a user and all their data from every system.

// Complete user deletion (what you need to implement in any boilerplate):
export async function deleteUserAndAllData(userId: string) {
  // Step 1: Cancel Stripe subscription
  const user = await db.user.findUnique({
    where: { id: userId },
    include: { subscription: true },
  });

  if (user?.subscription?.stripeSubscriptionId) {
    await stripe.subscriptions.cancel(user.subscription.stripeSubscriptionId);
    await stripe.customers.del(user.subscription.stripeCustomerId);
  }

  // Step 2: Delete from email service (Resend/SendGrid)
  if (user?.resendContactId) {
    await resend.contacts.remove({ id: user.resendContactId, audienceId: AUDIENCE_ID });
  }

  // Step 3: Delete from analytics (PostHog)
  if (process.env.POSTHOG_API_KEY) {
    await fetch(`https://your-posthog.com/api/person/`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.POSTHOG_API_KEY}` },
      body: JSON.stringify({ distinct_ids: [userId] }),
    });
  }

  // Step 4: Delete files from storage (Supabase/S3)
  await supabase.storage.from('user-files').list(userId).then(async ({ data }) => {
    const paths = data?.map((f) => `${userId}/${f.name}`) ?? [];
    if (paths.length) await supabase.storage.from('user-files').remove(paths);
  });

  // Step 5: Delete from database (CASCADE deletes handle related records):
  await db.user.delete({ where: { id: userId } });

  // Step 6: Anonymize audit logs (delete identity, keep event)
  await db.auditLog.updateMany({
    where: { userId },
    data: { userId: null, email: '[deleted]', ipAddress: null },
  });
}

Why Prisma/Drizzle schemas matter for GDPR:

// ✅ GDPR-friendly schema — cascade deletes propagate:
model User {
  id            String   @id @default(cuid())
  email         String   @unique
  subscriptions Subscription[]
  sessions      Session[]
  posts         Post[]
  files         File[]
  // When user is deleted: all related records auto-delete
}

model Subscription {
  userId  String
  user    User   @relation(fields: [userId], references: [id], onDelete: Cascade)
}

// ❌ GDPR-unfriendly — orphaned records after deletion:
model Subscription {
  userId  String
  user    User   @relation(fields: [userId], references: [id])
  // onDelete defaults to Restrict — deletion FAILS if subscription exists
}

2. Data Export (Right to Access)

// app/api/user/data-export/route.ts
export async function GET(req: Request) {
  const session = await auth();
  if (!session) return new Response('Unauthorized', { status: 401 });

  const userId = session.user.id;

  // Collect all user data:
  const [user, subscriptions, posts, files] = await Promise.all([
    db.user.findUnique({ where: { id: userId } }),
    db.subscription.findMany({ where: { userId } }),
    db.post.findMany({ where: { userId } }),
    db.file.findMany({ where: { userId } }),
  ]);

  const exportData = {
    exportDate: new Date().toISOString(),
    profile: {
      id: user?.id,
      email: user?.email,
      name: user?.name,
      createdAt: user?.createdAt,
    },
    subscriptions: subscriptions.map((s) => ({
      plan: s.plan,
      status: s.status,
      startDate: s.createdAt,
    })),
    content: posts,
    files: files.map((f) => ({ name: f.name, size: f.size, createdAt: f.createdAt })),
  };

  return new Response(JSON.stringify(exportData, null, 2), {
    headers: {
      'Content-Type': 'application/json',
      'Content-Disposition': `attachment; filename="my-data-${Date.now()}.json"`,
    },
  });
}

All boilerplates need a cookie consent manager added — none ship with one:

// Minimal consent manager (add to any boilerplate):
// components/ConsentBanner.tsx
'use client';
import { useState, useEffect } from 'react';

type ConsentPreferences = {
  necessary: true;          // Always true, can't be turned off
  analytics: boolean;
  marketing: boolean;
};

export function ConsentBanner() {
  const [showBanner, setShowBanner] = useState(false);
  const [prefs, setPrefs] = useState<ConsentPreferences>({
    necessary: true,
    analytics: false,
    marketing: false,
  });

  useEffect(() => {
    const stored = localStorage.getItem('consent');
    if (!stored) setShowBanner(true);
  }, []);

  const acceptAll = () => {
    const consent = { necessary: true, analytics: true, marketing: true };
    localStorage.setItem('consent', JSON.stringify(consent));
    setShowBanner(false);
    initAnalytics(); // Only load analytics after consent
  };

  const acceptNecessary = () => {
    const consent = { necessary: true, analytics: false, marketing: false };
    localStorage.setItem('consent', JSON.stringify(consent));
    setShowBanner(false);
  };

  if (!showBanner) return null;

  return (
    <div className="fixed bottom-0 left-0 right-0 bg-white border-t p-4 shadow-lg z-50">
      <p className="text-sm text-gray-600 mb-3">
        We use cookies to improve your experience. Analytics cookies help us understand
        how you use our product.{' '}
        <a href="/privacy" className="underline">Privacy policy</a>
      </p>
      <div className="flex gap-2">
        <button
          onClick={acceptAll}
          className="px-4 py-2 bg-black text-white rounded text-sm"
        >
          Accept all
        </button>
        <button
          onClick={acceptNecessary}
          className="px-4 py-2 border rounded text-sm"
        >
          Necessary only
        </button>
      </div>
    </div>
  );
}

// Only load analytics after consent:
export function initAnalytics() {
  if (typeof window === 'undefined') return;
  const consent = JSON.parse(localStorage.getItem('consent') ?? '{}');
  if (consent.analytics) {
    // Load PostHog, GA, etc.
    import('./analytics').then(({ loadPostHog }) => loadPostHog());
  }
}

Or use a third-party consent manager: CookieYes ($10/month), Cookiebot ($12/month), or Termly (free tier). These handle auto-blocking scripts based on consent — more reliable than DIY.

4. EU Data Residency

# Supabase — choose EU region at project creation:
# Europe (Frankfurt): eu-central-1
# Europe (London): eu-west-2

# Configure in .env:
NEXT_PUBLIC_SUPABASE_URL="https://[ref].supabase.co"
# Choose EU region during project creation in Supabase dashboard

# Vercel — set region:
# vercel.json:
{
  "regions": ["fra1"]  # Frankfurt
}

# Or in Vercel dashboard: Settings → Functions → Region → Frankfurt (fra1)

Boilerplate Rankings for GDPR

Supastarter — Best GDPR Foundation

Why it's best for GDPR:

  1. Row Level Security pre-written — data isolation between organizations is enforced at the database level (users can't access other orgs' data, even with SQL injection)
  2. Supabase EU region support — point to Frankfurt during setup
  3. Complete Prisma-style cascade deletes in migrations
  4. SSR auth means auth tokens never stored in localStorage (XSS safe)

What you still need to add:

  • Cookie consent manager (not included)
  • Data export endpoint
  • Complete deletion flow including Stripe/email service cleanup

T3 Stack — Most Controllable

Why T3 is good for GDPR:

  • Prisma schema fully in your control — design cascade deletes from day one
  • No mandatory third-party services baked in
  • Self-host on Railway/Render in EU region easily
  • NextAuth self-hosted = no user data at auth0.com/clerk.com

GDPR additions needed:

// Add to prisma/schema.prisma — cascade deletes:
model User {
  // Add to every relation:
  posts Post[] @relation(onDelete: "Cascade")
  files File[] @relation(onDelete: "Cascade")
  sessions Session[] @relation(onDelete: "Cascade")
}

ShipFast — Average GDPR Support

ShipFast doesn't have pre-written RLS (no multi-tenancy), but for B2C SaaS (one user = their own data), GDPR is actually simpler — you only need deletion, export, and consent.

ShipFast GDPR gaps:

  • MongoDB option: cascade deletes require middleware or manual deletion
  • No built-in data export
  • Analytics (Plausible) is privacy-friendly — points in ShipFast's favor

Self-Hosted Supabase + Any Boilerplate — Maximum Privacy

If GDPR compliance is a hard requirement and you have data sovereignty concerns:

# Self-host Supabase with Docker:
git clone https://github.com/supabase/supabase
cd supabase
cp .env.example .env
# Configure SMTP, JWT secret, etc.
docker compose up -d

# Your data never leaves your server
# You control every aspect of data processing
# GDPR Article 28 DPA needed with hosting provider, not Supabase

Third-Party Services Checklist

When building a GDPR-compliant SaaS, sign DPAs with every vendor:

ServiceDPA AvailableData LocationGDPR-Safe
SupabaseEU regions available
StripeEU regions
ResendUS (EU option)✅ with DPA
PostHog (self-hosted)N/AYour server
PostHog (cloud EU)EU
VercelEU regions
ClerkUS + EU✅ with DPA
Plausible (self-hosted)N/AYour server

The GDPR Setup Checklist for Any Boilerplate

Database:
[ ] Cascade deletes on all user relations (Prisma onDelete: Cascade)
[ ] No user PII in log files
[ ] Audit logs anonymized on deletion (keep event, remove identity)

API / Backend:
[ ] DELETE /api/user/account endpoint implemented
[ ] GET /api/user/data-export endpoint implemented
[ ] Auth tokens in httpOnly cookies (not localStorage)
[ ] No unnecessary data collected at signup

Frontend:
[ ] Cookie consent banner (not pre-checked boxes)
[ ] Analytics only loaded after consent
[ ] Privacy policy linked from signup and footer
[ ] Cookie policy listing all cookies used

Infrastructure:
[ ] EU region selected (Vercel fra1, Supabase eu-central-1)
[ ] DPAs signed with all vendors
[ ] Data retention policy defined (delete inactive accounts after X days)

Legal:
[ ] Privacy policy drafted (use Iubenda, Termly, or a lawyer)
[ ] Terms of service updated
[ ] Stripe DPA signed in Stripe dashboard
[ ] Supabase DPA signed in Supabase dashboard

Find boilerplates by compliance features at StarterPick.

EU Data Residency in Depth: What "EU Region" Actually Means

Selecting an EU region for your hosting provider is the beginning of data residency compliance, not the end. Understanding what customer data flows where is necessary for complete GDPR compliance.

When you deploy to Vercel's Frankfurt region (fra1), your serverless function runs in Frankfurt. But Vercel's edge network — the CDN layer that serves static assets and runs Edge Middleware — is distributed globally. A user in Germany who requests your home page may get it from Vercel's CDN node in Frankfurt, but they might also get it from a node in London, Amsterdam, or elsewhere depending on routing. Static assets don't contain personal data, so this is generally not a problem. But Edge Middleware that reads authentication cookies and makes authorization decisions runs globally — technically processing personal data (the user's session token) outside the EU when it runs on a non-EU edge node.

The practical compliance approach: store personal data (database records, file uploads) in EU regions, and understand that session processing at the edge may occur globally for performance. Most DPAs with hosting providers like Vercel and Cloudflare include Standard Contractual Clauses (SCCs) that make this cross-border processing compliant. Signing the Vercel DPA and confirming SCCs are included is more important than ensuring every byte processes in Frankfurt.

Supabase's EU regions (Frankfurt eu-central-1, London eu-west-2) are straightforward: your PostgreSQL data, Auth users, and Storage files all live in the selected region. There's no Supabase CDN that distributes personal data globally. For highly regulated industries (healthcare, financial services), Supabase in an EU region with a signed DPA satisfies most data residency requirements without additional configuration.

Self-hosting Supabase on a Hetzner Falkenstein (fsn1) server is the strongest data sovereignty story: your data never leaves a server you control in Germany. The operational overhead is real — you manage PostgreSQL updates, Supabase service updates, backup verification, and disaster recovery yourself. For most SaaS products, managed Supabase with signed DPA is the right trade-off. For highly sensitive personal data (medical records, financial data) where breach liability is severe, self-hosting is worth the overhead.

Row Level Security as GDPR Infrastructure

Supabase's Row Level Security (RLS) does double duty: it prevents data leaks between customers (important for multi-tenant SaaS) and it creates an auditable record of who can access what data (important for GDPR's accountability requirements).

RLS policies in PostgreSQL attach authorization logic to the database itself rather than to the application layer. Even if your application code has an authorization bug that returns data without checking the authenticated user's permissions, RLS blocks the database from returning rows the authenticated user doesn't own. This matters for GDPR because the "privacy by design" requirement (Article 25) asks for technical measures that protect personal data by default — RLS is one of the strongest such measures.

A simple RLS policy for user data isolation:

-- Enable RLS on the table
ALTER TABLE user_documents ENABLE ROW LEVEL SECURITY;

-- Users can only see their own documents
CREATE POLICY "users_see_own_documents"
ON user_documents
FOR ALL
USING (auth.uid() = user_id);

For GDPR compliance, RLS also simplifies audit responses. If a customer submits a Subject Access Request (Article 15) asking "what data do you hold about me?", your data export endpoint uses the same RLS context — you can be confident the export contains all their data and only their data. The export is scoped by the authenticated user_id at the database level, not by application-layer filtering that could have blind spots.

Supastarter's pre-written RLS policies for organization-level tenancy are the reason it ranks first for GDPR compliance. Every table that contains organizational data has RLS policies enforcing tenant isolation. The policies are tested in the boilerplate's test suite. Adopting them means the most common GDPR-related data isolation requirement is handled before you write a single line of product code.

Automating GDPR Requests: The Technical Build-Out

Beyond deletion and export, GDPR's accountability requirements suggest building automated handling for the full range of data subject rights. Manual handling of requests (responding to support tickets with data exports) doesn't scale and creates documentation gaps.

A compliance-aware SaaS builds a self-service portal within the account settings for three rights: right to access (download your data), right to erasure (delete your account and all data), and right to restriction (pause processing — rarer, but required). The first two are typically 1–2 days of development per boilerplate.

The right to access endpoint should generate a ZIP file containing all the user's data in machine-readable format (GDPR requires portability — JSON is standard). The ZIP should include: profile information, all records they've created or contributed to, subscription history, communication preferences, and consent records. Generating this in real time is acceptable for small accounts; for accounts with large data volumes, queue the generation as a background job and email the download link.

The deletion endpoint is more complex because deletion is final and must cascade through all data stores, not just the primary database. The pattern covered earlier in this article handles the database and service-level cleanup. The audit trail requirement under GDPR adds a nuance: you must delete personal data, but you may need to retain anonymized records for legal or financial reasons. An audit log that recorded "user X upgraded to Pro" should retain the event with the user ID replaced by [deleted user] after erasure — the financial event happened, the identity should not persist.

Document every data subject request in an internal log: who requested, what type, when received, when completed, what systems were affected. GDPR's accountability principle (Article 5(2)) requires you to demonstrate compliance, not just achieve it. A spreadsheet or simple database table tracking these requests is sufficient for most SaaS products until the volume justifies a dedicated compliance tool.


GDPR Breach Notification: The 72-Hour Technical Requirement

GDPR Article 33 requires notifying your supervisory authority within 72 hours of becoming aware of a personal data breach. The "becoming aware" trigger matters — you're not on the clock until you know about the breach, but ignorance because of inadequate monitoring is not a defense.

The technical prerequisites for 72-hour notification are: knowing when a breach occurred (log aggregation and anomaly detection), being able to assess what data was affected (data inventory and classification), and having a communication pathway to the supervisory authority (typically a web portal for most EU countries). None of these require complex tooling, but they do require deliberate setup.

A minimal breach detection system for SaaS: ship application logs to a log aggregation service (BetterStack, Axiom, or Datadog), configure alerts for anomalous authentication patterns (many failed logins from a new IP, large data exports at unusual hours), and test the alert pipeline at least monthly. The alert doesn't detect every breach type, but it catches the most common ones: credential stuffing, data exfiltration, and unauthorized admin access.

Your incident response runbook should be written before you need it. It should include: who is notified internally (CTO, legal counsel if applicable), how to assess scope (which user records, which data fields, what time window), how to preserve forensic evidence (database logs, access logs), and the exact URL for filing with your relevant supervisory authority. GDPR doesn't require you to have a complex incident response program — it requires a documented process that you actually follow. A two-page document in your company wiki is sufficient.

Vendor Risk Management Under GDPR

GDPR's accountability requirements extend to your vendors. Every service that processes personal data on your behalf requires a Data Processing Agreement (DPA). This is not optional — operating without DPAs with your vendors is a GDPR violation regardless of how well your own systems are configured.

The practical list for a typical Next.js SaaS: Vercel (DPA in the dashboard under Team Settings → Legal), Supabase (DPA in the dashboard), Stripe (DPA in the Stripe dashboard under Settings → Legal), Resend (DPA available on request), Sentry (DPA in organization settings), PostHog (DPA available in account settings for cloud; self-hosted requires no DPA). Most major SaaS providers offer DPAs and have made signing straightforward. The DPA signing process takes 30–60 minutes for a typical stack.

New vendor onboarding should include a DPA check as a required step. A shared document or Notion page listing every vendor, its DPA status, the data categories it processes, and the data residency should be maintained. For enterprise customers asking about your sub-processors, this document is your answer. Updating it quarterly takes 30 minutes and keeps you audit-ready.

The sub-processor disclosure requirement means your customers deserve to know which vendors you use to process their data. A /privacy/sub-processors page listing your vendors and their purposes is good practice and required by most enterprise customers' security review processes. Many SaaS companies ship this before they have enterprise customers — it signals maturity to technical buyers evaluating your product.


Read the when to outgrow your boilerplate guide — GDPR data isolation requirements are one of the most common reasons teams outgrow single-tenant boilerplate foundations.

See best multi-tenant boilerplates for boilerplates with pre-built organization isolation that simplifies GDPR compliance.

Browse boilerplates by compliance and security features in the best SaaS boilerplates 2026 guide.

The SaaS Boilerplate Matrix (Free PDF)

20+ SaaS starters compared: pricing, tech stack, auth, payments, and what you actually ship with. Updated monthly. Used by 150+ founders.

Join 150+ SaaS founders. Unsubscribe in one click.