Comment Lancer un SaaS en 2026 : Le Guide Complet avec React + Symfony
Meta description: Découvrez comment créer un SaaS de zéro en 2026 avec React et Symfony. Architecture, auth, paiements Stripe, déploiement Docker — guide complet + boilerplate prêt à l'emploi.Introduction : Pourquoi la plupart des devs perdent 3 mois avant d'avoir une première version
Vous avez une idée de SaaS. Vous savez coder. Mais avant d'écrire la première ligne de logique métier, il faut mettre en place :
- L'authentification (JWT, refresh tokens, forgot password...)
- La gestion des utilisateurs et des organisations
- Les paiements Stripe avec webhooks
- Le dashboard admin
- Le déploiement Docker + CI/CD
- Le monitoring et les logs
Ce guide vous explique comment construire cette infrastructure correctement dès le départ — et comment ne plus jamais perdre ce temps.
Partie 1 : L'architecture SaaS en 2026
Frontend vs Backend : pourquoi séparer ?
En 2026, la séparation frontend/backend n'est plus optionnelle pour un SaaS sérieux :
Frontend React (TypeScript)- Interface réactive, rapide, mobile-first
- State management clair (React Query pour le serveur, Zustand pour le local)
- Indépendant du backend — peut être déployé sur CDN
- API REST ou GraphQL
- Gestion des sessions, permissions, webhooks
- Code métier isolé et testable
La structure de base
saas-project/
├── frontend/ # React 18 + TypeScript + Vite
│ ├── src/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── hooks/
│ │ ├── contexts/ # Auth, Theme, etc.
│ │ └── lib/ # API client, utils
├── backend/ # Symfony 7 + API Platform
│ ├── src/
│ │ ├── Entity/
│ │ ├── Controller/
│ │ ├── Service/
│ │ └── Security/
├── nginx/ # Config reverse proxy
└── docker-compose.yml
Partie 2 : L'authentification — le composant le plus critique
JWT vs Sessions : que choisir ?
Pour un SaaS moderne avec une SPA React, JWT est le standard :
// AuthContext.tsx — le cœur de votre auth
interface AuthContextType {
user: User | null;
login: (credentials: LoginCredentials) => Promise<void>;
logout: () => void;
isLoading: boolean;
}
export const AuthContext = createContext<AuthContextType | null>(null);
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// Vérifie le token au démarrage
const token = localStorage.getItem('access_token');
if (token) {
validateToken(token).then(setUser).finally(() => setIsLoading(false));
} else {
setIsLoading(false);
}
}, []);
// ...
};
Ce qu'il faut absolument implémenter :
/login)Côté Symfony
// src/Security/JwtAuthenticator.php
class JwtAuthenticator extends AbstractAuthenticator
{
public function authenticate(Request $request): Passport
{
$token = $this->extractToken($request);
try {
$payload = $this->jwtService->decode($token);
} catch (JwtException $e) {
throw new AuthenticationException('Invalid token');
}
return new SelfValidatingPassport(
new UserBadge($payload['email'])
);
}
}
Partie 3 : Les paiements Stripe
Setup en 4 étapes
Étape 1 — Créer les produits dans Stripe Dashboard- Un produit par plan (Starter, Pro, Enterprise)
- Prix récurrents (mensuel/annuel)
- Notez les
price_idpour votre code
// src/Controller/PaymentController.php
#[Route('/create-checkout', methods: ['POST'])]
public function createCheckout(Request $request): JsonResponse
{
$session = $this->stripe->checkout->sessions->create([
'payment_method_types' => ['card'],
'line_items' => [[
'price' => $request->get('price_id'),
'quantity' => 1,
]],
'mode' => 'subscription',
'success_url' => $this->getParameter('app_url') . '/success?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $this->getParameter('app_url') . '/pricing',
'metadata' => [
'user_id' => $this->getUser()->getId(),
],
]);
return $this->json(['url' => $session->url]);
}
Étape 3 — Webhook pour confirmer le paiement
#[Route('/webhook/stripe', methods: ['POST'])]
public function stripeWebhook(Request $request): Response
{
$event = Webhook::constructEvent(
$request->getContent(),
$request->headers->get('Stripe-Signature'),
$this->stripeWebhookSecret
);
switch ($event->type) {
case 'checkout.session.completed':
$this->handleCheckoutCompleted($event->data->object);
break;
case 'customer.subscription.deleted':
$this->handleSubscriptionCancelled($event->data->object);
break;
}
return new Response('OK', 200);
}
Étape 4 — Portail client Stripe
Pas besoin de développer une page de gestion abonnement — Stripe le fait pour vous :
$session = $this->stripe->billingPortal->sessions->create([
'customer' => $user->getStripeCustomerId(),
'return_url' => $this->generateUrl('dashboard'),
]);
Partie 4 : Le déploiement Docker
docker-compose.yml de production
version: '3.8'
services:
frontend:
build:
context: ./frontend
target: production
restart: unless-stopped
backend:
build: ./backend
environment:
DATABASE_URL: postgresql://user:pass@db:5432/saas
JWT_SECRET: ${JWT_SECRET}
STRIPE_KEY: ${STRIPE_SECRET_KEY}
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/ssl/certs
depends_on:
- frontend
- backend
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres_data:
GitHub Actions CI/CD
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run frontend tests
run: cd frontend && npm ci && npm test
- name: Run backend tests
run: cd backend && composer install && php bin/phpunit
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: deploy
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/saas
git pull origin main
docker-compose up -d --build
Partie 5 : Les erreurs courantes à éviter
1. Ne pas séparer les environnements
Avoir un .env.local pour le dev et un .env.production pour la prod — c'est la base. Ne jamais hardcoder des clés API.
2. Oublier le rate limiting
Un endpoint /login sans rate limiting = invitation aux attaques brute force. Symfony a symfony/rate-limiter.
3. Ne pas penser à la scalabilité dès le départ
- Images uploadées → S3 (pas le disque local)
- Sessions → Redis (pas la base de données)
- Emails → queue (pas synchrone dans le controller)
4. Déployer sans monitoring
Vous ne saurez pas que votre app est down avant que les clients vous le disent. Utilisez au minimum :
- Uptime Robot (gratuit)
- Sentry pour les erreurs (tier gratuit généreux)
- Logs structurés (Monolog)
Partie 6 : Combien de temps ça prend vraiment ?
| Composant | De zéro | Avec un boilerplate |
|-----------|---------|---------------------|
| Auth (JWT + refresh + forgot password) | 3-5 jours | 0 (déjà fait) |
| Paiements Stripe | 2-3 jours | 0 (déjà fait) |
| Dashboard admin | 3-4 jours | 0 (déjà fait) |
| Docker + CI/CD | 1-2 jours | 0 (déjà fait) |
| Landing page | 2-3 jours | 0 (déjà fait) |
| Total infra | 2-4 semaines | < 1 heure |
La vraie question : Voulez-vous passer 3 semaines à configurer de l'infrastructure, ou à construire la vraie valeur de votre SaaS ?Conclusion : Le bon point de départ
Lancer un SaaS en 2026 demande une solide base technique. L'architecture React + Symfony reste un choix excellent : TypeScript sur le frontend pour la robustesse, Symfony pour la structure et la sécurité côté API.
Le plus difficile n'est pas de savoir comment faire — c'est de trouver le temps de tout mettre en place correctement.
Si vous voulez démarrer avec une base solide, le SaaS Boilerplate React + Symfony contient tout ce dont vous avez besoin : auth complète, Stripe, Docker, CI/CD GitHub Actions, dashboard admin —npm install && npm run dev et vous êtes prêt en moins d'une heure.
FAQ
Q : React + Symfony, c'est une bonne stack pour un SaaS en 2026 ?Oui. React reste le framework frontend dominant (65% des offres d'emploi frontend en France). Symfony est utilisé par les entreprises qui ont besoin de robustesse et de sécurité. La combinaison est battle-tested.
Q : Est-ce que je dois connaître Symfony pour utiliser le boilerplate ?Oui, un niveau intermédiaire est nécessaire. Le boilerplate est pour des développeurs, pas des débutants complets.
Q : Pourquoi pas Next.js + Laravel ?Les deux fonctionnent très bien. React + Symfony est plus adapté si vous avez déjà de l'expérience PHP/Symfony dans votre équipe.
Q : Est-ce qu'il y a du multi-tenant dans le boilerplate ?La structure est préparée pour le multi-tenant (organisations + membres) mais l'implémentation complète est à votre charge selon votre modèle.
Q : Quel hébergement recommandez-vous ?Pour démarrer : un VPS 8 Go RAM (Hetzner ou OVH, ~10-15€/mois). Docker-compose tourne sans problème. Pour scaler : DigitalOcean App Platform ou AWS ECS.
Q : Les paiements Stripe sont en mode test ou production ?Le boilerplate inclut les deux configurations. Les clés Stripe test sont dans .env.local, les clés prod dans .env.production.
Licence commerciale — vous pouvez l'utiliser pour autant de projets que vous voulez, mais pas le revendre tel quel.
Cet article fait partie de la série "Lancer un SaaS en 2026" sur NetRevision.