A modern, production-ready SaaS template built with SvelteKit 2, Svelte 5, Supabase, and Skeleton UI. Get your SaaS project up and running in hours, not months.
- π Sveltey - a SaaS Template built with SvelteKit
- Supabase Auth Integration: Complete authentication flow with email/password and OAuth providers
- Protected Routes: Automatic route protection and session management
- User Profiles: Customizable user profile management
- Password Reset: Secure password reset functionality
- Skeleton UI: Beautiful, accessible components out of the box
- Dark/Light Mode: Built-in mode switching
- Comprehensive Themes: 22 beautiful handmade themes
- Responsive Design: Mobile-first, responsive layouts
- Loading States: Elegant loading indicators and transitions
- Toast Notifications: User-friendly feedback system
- Blog System: Built-in blog with markdown support
- SEO Optimized: Meta tags, Open Graph, and structured data
- Admin Dashboard: Clean, intuitive admin interface
- User Analytics: Basic user metrics and insights
- Real-time Updates: Live data updates using Supabase real-time
- Data Visualization: Charts and graphs for key metrics
- Privacy-First Analytics: Built-in Plausible Analytics integration for GDPR-compliant tracking
- Stripe Integration: Ready-to-use payment processing
- Subscription Plans: Flexible pricing tiers
- Billing Management: Customer billing portal
- Webhook Handling: Secure webhook processing
# Clone the repository git clone https://github.com/yourusername/sveltey.git cd sveltey # Install dependencies npm install # Set up environment variables cp .env.example .env # Edit .env with your Supabase and Stripe keys # Start the development server npm run devVisit http://localhost:5173 and start building your SaaS!
- Framework: SvelteKit 2 with Svelte 5
- Database: Supabase (PostgreSQL)
- Authentication: Supabase Auth
- UI Components: Skeleton UI
- Styling: Tailwind CSS
- Analytics: Plausible Analytics
- Email: Resend
- Deployment: Vercel/Netlify ready
- Language: TypeScript
src/ βββ lib/ β βββ components/ # Reusable UI components β βββ stores/ # Svelte stores for state management β βββ utils/ # Utility functions β βββ types/ # TypeScript type definitions βββ routes/ β βββ (app)/ # App routes | | βββ api # API routes, security is handled at the endpoint level | | βββ app # App routes, security is handeled globally, all sub-routes are protected | | βββ auth # Auth routes, used for login, logout, and password reset functionality β βββ (marketing)/ # Public marketing pages | βββ blog # API routes, security is handled at the endpoint level | βββ contact # Contact page | βββ pricing # Pricing page | βββ privacy # Privacy policy page | βββ terms # Terms of service page βββ app.html # App shell βββ app.css # Global styles βββ hooks.server.ts # Server hooks Create a .env file in the root directory:
# Supabase PUBLIC_SUPABASE_URL=your_supabase_url PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key # Resend (for email) RESEND_API_KEY=your_resend_api_key # Stripe // coming soon PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key STRIPE_SECRET_KEY=your_stripe_secret_key STRIPE_WEBHOOK_SECRET=your_webhook_secret - Create a new Supabase project
- Copy the project URL and anon key
- Run the included SQL migrations
- Set up your authentication providers
Sveltey uses Resend for reliable email delivery with excellent developer experience.
- Create a Resend account at resend.com
- Get your API key from the Resend dashboard
- Add to environment variables:
RESEND_API_KEY=re_your_api_key_here
- Verify your domain (optional but recommended for production):
- Add your domain in the Resend dashboard
- Configure DNS records as instructed
- This removes the "via resend.com" branding and improves deliverability
- Transactional Emails: Password resets, welcome emails, notifications
- Template Support: Beautiful HTML email templates
- Delivery Tracking: Monitor email delivery and engagement
- High Deliverability: Excellent inbox placement rates
- Simple API: Easy integration with SvelteKit API routes
// src/routes/api/send-email/+server.ts import { RESEND_API_KEY } from '$env/static/private'; import { Resend } from 'resend'; const resend = new Resend(RESEND_API_KEY); export async function POST({ request }) { const { to, subject, html } = await request.json(); try { const data = await resend.emails.send({ from: 'noreply@yourdomain.com', to, subject, html }); return new Response(JSON.stringify({ success: true, data }), { headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } }Create reusable email templates in src/lib/emails/:
// src/lib/emails/welcome.ts export const welcomeEmail = (userName: string) => ` <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;"> <h1 style="color: #333;">Welcome to Sveltey, ${userName}!</h1> <p>Thank you for joining our platform. We're excited to have you on board.</p> <a href="https://yourdomain.com/dashboard" style="background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;"> Get Started </a> </div> `;- Create a Stripe account
- Copy your API keys
- Configure your products and pricing
- Set up webhooks for subscription events
Sveltey comes pre-configured with Plausible Analytics - a lightweight, privacy-focused analytics solution that's GDPR compliant by default.
The analytics script is already integrated in src/app.html:
<script defer data-domain="sveltey.dev" src="https://events.plygrnd.org/js/script.js"></script>- Self-hosted Plausible: If using your own Plausible instance, update the script source
- Plausible Cloud: Change to the official Plausible script:
<script defer data-domain="yourdomain.com" src="https://plausible.io/js/script.js"></script>
- Update Domain: Replace
data-domain="sveltey.dev"with your actual domain
- Privacy-First: No cookies, no personal data collection
- GDPR Compliant: No consent banners required
- Lightweight: Less than 1KB script size
- Real-time: Live visitor tracking
- Goal Tracking: Custom event tracking available
Add custom event tracking for user actions:
// Track custom events plausible('signup', {props: {plan: 'premium'}}); plausible('purchase', {props: {amount: 99}});To use a different analytics provider:
- Remove the Plausible script from
src/app.html - Add your preferred analytics script (Google Analytics, Fathom, etc.)
- Update the privacy policy accordingly
- Update
app.htmlfor favicon and meta tags - Modify
src/lib/config.tsfor app configuration - Customize colors in with a custom Skeleton UI theme
- Replace logo
- All components are in
src/lib/components/ - Skeleton UI provides the base component library
- Easy to theme and customize with CSS variables
Sveltey comes with a comprehensive SEO system built on top of svelte-meta-tags that provides automatic meta tag management, OpenGraph support, and Twitter Card integration.
The meta tag system in Sveltey uses a two-level approach:
- Base Meta Tags (
src/routes/+layout.ts) - Global defaults for your entire site - Page Meta Tags (
src/routes/*/+page.ts) - Page-specific overrides and additions
The base meta tags are defined in src/routes/+layout.ts and include:
const baseMetaTags = Object.freeze({ title: 'Sveltey - SvelteKit SaaS Template', titleTemplate: '%s | Sveltey', description: 'Your default site description...', canonical: new URL(url.pathname, url.origin).href, robots: 'index,follow', keywords: ['SvelteKit', 'SaaS', 'template'], openGraph: { type: 'website', url: new URL(url.pathname, url.origin).href, title: 'Sveltey - SvelteKit SaaS Template', description: 'Your OpenGraph description...', siteName: 'Sveltey', locale: 'en_US', images: [ { url: `${url.origin}/og-image.jpg`, width: 1200, height: 630, alt: 'Sveltey - SvelteKit SaaS Template', type: 'image/jpeg' } ] }, twitter: { cardType: 'summary_large_image', site: '@sveltey_dev', creator: '@sveltey_dev', title: 'Sveltey - SvelteKit SaaS Template', description: 'Your Twitter description...', image: `${url.origin}/og-image.jpg`, imageAlt: 'Sveltey - SvelteKit SaaS Template' } }) satisfies MetaTagsProps;Each page can override and extend the base meta tags by exporting a load function in its +page.ts file:
// src/routes/your-page/+page.ts import type { MetaTagsProps } from 'svelte-meta-tags'; export const load = () => { const pageMetaTags = Object.freeze({ title: 'Your Page Title', description: 'Specific description for this page', keywords: ['additional', 'keywords', 'for', 'this', 'page'], openGraph: { title: 'Your Page Title - Brand Name', description: 'OpenGraph description for social sharing', type: 'article', // or 'website', 'product', etc. images: [ { url: 'https://your-domain.com/specific-og-image.jpg', width: 1200, height: 630, alt: 'Description of your image' } ] }, twitter: { title: 'Twitter-specific title', description: 'Twitter-specific description' }, // Additional meta tags additionalMetaTags: [ { name: 'author', content: 'Your Name' }, { property: 'article:published_time', content: '2024-01-01T00:00:00Z' } ] }) satisfies MetaTagsProps; return { pageMetaTags }; };For dynamic pages (like blog posts), you can generate meta tags based on content:
// src/routes/blog/[slug]/+page.server.ts export const load = async ({ params, url }) => { const post = await getPostBySlug(params.slug); const pageMetaTags = Object.freeze({ title: post.title, description: post.excerpt, canonical: new URL(`/blog/${params.slug}`, url.origin).href, openGraph: { type: 'article', title: post.title, description: post.excerpt, url: new URL(`/blog/${params.slug}`, url.origin).href, images: post.featuredImage ? [ { url: post.featuredImage, width: 1200, height: 630, alt: post.title } ] : undefined, article: { publishedTime: post.publishedAt, authors: [post.author], section: 'Technology', tags: post.tags } } }) satisfies MetaTagsProps; return { post, pageMetaTags }; };Control search engine indexing per page:
const pageMetaTags = { robots: 'noindex,nofollow', // Don't index this page // or robots: 'index,follow', // Index this page (default) // or robots: 'index,nofollow' // Index but don't follow links };OpenGraph images are crucial for social media sharing and SEO. Sveltey provides a flexible system for managing these images.
Place your default OpenGraph image in the static folder:
static/ βββ og-image.jpg # Default 1200x630 image βββ og-image-square.jpg # Optional square variant βββ favicon.png The default image is automatically referenced in your base meta tags:
// src/routes/+layout.ts openGraph: { images: [ { url: `${url.origin}/og-image.jpg`, width: 1200, height: 630, alt: 'Sveltey - SvelteKit SaaS Template', type: 'image/jpeg' } ] }Override the default image for specific pages:
// src/routes/pricing/+page.ts const pageMetaTags = { openGraph: { images: [ { url: `${url.origin}/og-pricing.jpg`, width: 1200, height: 630, alt: 'Sveltey Pricing Plans', type: 'image/jpeg' } ] } };For blog posts or dynamic content, you can generate or specify images dynamically:
// src/routes/blog/[slug]/+page.server.ts const pageMetaTags = { openGraph: { images: post.featuredImage ? [ { url: post.featuredImage, width: 1200, height: 630, alt: post.title, type: 'image/jpeg' } ] : [ { url: `${url.origin}/og-blog-default.jpg`, width: 1200, height: 630, alt: 'Sveltey Blog', type: 'image/jpeg' } ] } };- Recommended Size: 1200x630 pixels (1.91:1 aspect ratio)
- Minimum Size: 600x315 pixels
- Maximum Size: 8MB
- Format: JPG or PNG (JPG preferred for smaller file size)
// Example with multiple image variants openGraph: { images: [ { url: `${url.origin}/og-image-large.jpg`, width: 1200, height: 630, alt: 'Large image for Facebook, LinkedIn', type: 'image/jpeg' }, { url: `${url.origin}/og-image-square.jpg`, width: 1080, height: 1080, alt: 'Square image for Instagram, Twitter', type: 'image/jpeg' } ] }For advanced use cases, you can generate images dynamically:
// src/routes/api/og/[slug]/+server.ts export async function GET({ params, url }) { const post = await getPostBySlug(params.slug); // Generate image using libraries like @vercel/og or canvas const image = await generateOGImage({ title: post.title, author: post.author, template: 'blog-post' }); return new Response(image, { headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=31536000, immutable' } }); }Then reference it in your meta tags:
openGraph: { images: [ { url: `${url.origin}/api/og/${params.slug}`, width: 1200, height: 630, alt: post.title, type: 'image/png' } ] }Use these tools to test your OpenGraph implementation:
- Facebook Debugger: https://developers.facebook.com/tools/debug/
- Twitter Card Validator: https://cards-dev.twitter.com/validator
- LinkedIn Post Inspector: https://www.linkedin.com/post-inspector/
openGraph: { type: 'website', // website, article, product, etc. title: 'Page Title', // Specific title for social sharing description: 'Description', // Social media description url: 'https://example.com', // Canonical URL siteName: 'Site Name', // Your site/brand name locale: 'en_US', // Language and region // For articles article: { publishedTime: '2024-01-01T00:00:00Z', modifiedTime: '2024-01-02T00:00:00Z', authors: ['Author Name'], section: 'Technology', tags: ['svelte', 'sveltekit'] }, // For products product: { price: { amount: '29.99', currency: 'USD' } } }Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: Report bugs or request features via GitHub Issues
- SvelteKit - The amazing web framework
- Supabase - Backend as a Service
- Skeleton UI - UI component library
- Tailwind CSS - Utility-first CSS framework
- Stripe - Payment processing
Ready to launch your SaaS? Get started with Sveltey today!
